root/fs/ext2/truncate.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. trunc_direct
  2. trunc_indirect
  3. trunc_dindirect
  4. trunc_tindirect
  5. ext2_truncate

   1 /*
   2  *  linux/fs/ext2/truncate.c
   3  *
   4  *  Copyright (C) 1992, 1993, 1994  Remy Card (card@masi.ibp.fr)
   5  *                                  Laboratoire MASI - Institut Blaise Pascal
   6  *                                  Universite Pierre et Marie Curie (Paris VI)
   7  *
   8  *  from
   9  *
  10  *  linux/fs/minix/truncate.c
  11  *
  12  *  Copyright (C) 1991, 1992  Linus Torvalds
  13  */
  14 
  15 #include <linux/errno.h>
  16 #include <linux/fs.h>
  17 #include <linux/ext2_fs.h>
  18 #include <linux/fcntl.h>
  19 #include <linux/sched.h>
  20 #include <linux/stat.h>
  21 #include <linux/locks.h>
  22 
  23 #define clear_block(addr,size,value) \
  24         __asm__("cld\n\t" \
  25                 "rep\n\t" \
  26                 "stosl" \
  27                 : \
  28                 :"a" (value), "c" (size / 4), "D" ((long) (addr)) \
  29                 :"cx", "di")
  30 
  31 /*
  32  * Truncate has the most races in the whole filesystem: coding it is
  33  * a pain in the a**. Especially as I don't do any locking...
  34  *
  35  * The code may look a bit weird, but that's just because I've tried to
  36  * handle things like file-size changes in a somewhat graceful manner.
  37  * Anyway, truncating a file at the same time somebody else writes to it
  38  * is likely to result in pretty weird behaviour...
  39  *
  40  * The new code handles normal truncates (size = 0) as well as the more
  41  * general case (size = XXX). I hope.
  42  */
  43 
  44 static int trunc_direct (struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
  45 {
  46         int i, tmp;
  47         unsigned long * p;
  48         struct buffer_head * bh;
  49         int retry = 0;
  50         int blocks = inode->i_sb->s_blocksize / 512;
  51 #define DIRECT_BLOCK ((inode->i_size + inode->i_sb->s_blocksize - 1) / \
  52                         inode->i_sb->s_blocksize)
  53         int direct_block = DIRECT_BLOCK;
  54 
  55 repeat:
  56         for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++) {
  57                 p = inode->u.ext2_i.i_data + i;
  58                 tmp = *p;
  59                 if (!tmp)
  60                         continue;
  61                 if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL)
  62                         bh = getblk (inode->i_dev, tmp,
  63                                      inode->i_sb->s_blocksize);
  64                 else
  65                         bh = get_hash_table (inode->i_dev, tmp,
  66                                              inode->i_sb->s_blocksize);
  67                 if (i < direct_block) {
  68                         brelse (bh);
  69                         goto repeat;
  70                 }
  71                 if ((bh && bh->b_count != 1) || tmp != *p) {
  72                         retry = 1;
  73                         brelse (bh);
  74                         continue;
  75                 }
  76                 *p = 0;
  77                 inode->i_blocks -= blocks;
  78                 inode->i_dirt = 1;
  79                 if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
  80                         clear_block (bh->b_data, inode->i_sb->s_blocksize,
  81                                      CURRENT_TIME);
  82                         bh->b_dirt = 1;
  83                 }
  84                 brelse (bh);
  85                 ext2_free_blocks (inode->i_sb, tmp, 1);
  86         }
  87         return retry;
  88 }
  89 
  90 static int trunc_indirect (struct inode * inode, int offset, unsigned long * p)
     /* [previous][next][first][last][top][bottom][index][help] */
  91 {
  92         int i, tmp;
  93         struct buffer_head * bh;
  94         struct buffer_head * ind_bh;
  95         unsigned long * ind;
  96         int retry = 0;
  97         int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
  98         int blocks = inode->i_sb->s_blocksize / 512;
  99 #define INDIRECT_BLOCK ((int)DIRECT_BLOCK - offset)
 100         int indirect_block = INDIRECT_BLOCK;
 101 
 102         tmp = *p;
 103         if (!tmp)
 104                 return 0;
 105         ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
 106         if (tmp != *p) {
 107                 brelse (ind_bh);
 108                 return 1;
 109         }
 110         if (!ind_bh) {
 111                 *p = 0;
 112                 return 0;
 113         }
 114 repeat:
 115         for (i = indirect_block ; i < addr_per_block ; i++) {
 116                 if (i < 0)
 117                         i = 0;
 118                 if (i < indirect_block)
 119                         goto repeat;
 120                 ind = i + (unsigned long *) ind_bh->b_data;
 121                 tmp = *ind;
 122                 if (!tmp)
 123                         continue;
 124                 if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL)
 125                         bh = getblk (inode->i_dev, tmp,
 126                                      inode->i_sb->s_blocksize);
 127                 else
 128                         bh = get_hash_table (inode->i_dev, tmp,
 129                                              inode->i_sb->s_blocksize);
 130                 if (i < indirect_block) {
 131                         brelse (bh);
 132                         goto repeat;
 133                 }
 134                 if ((bh && bh->b_count != 1) || tmp != *ind) {
 135                         retry = 1;
 136                         brelse (bh);
 137                         continue;
 138                 }
 139                 *ind = 0;
 140                 ind_bh->b_dirt = 1;
 141                 if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
 142                         clear_block (bh->b_data, inode->i_sb->s_blocksize,
 143                                      CURRENT_TIME);
 144                         bh->b_dirt = 1;
 145                 }
 146                 brelse (bh);
 147                 ext2_free_blocks (inode->i_sb, tmp, 1);
 148                 inode->i_blocks -= blocks;
 149                 inode->i_dirt = 1;
 150         }
 151         ind = (unsigned long *) ind_bh->b_data;
 152         for (i = 0; i < addr_per_block; i++)
 153                 if (*(ind++))
 154                         break;
 155         if (i >= addr_per_block)
 156                 if (ind_bh->b_count != 1)
 157                         retry = 1;
 158                 else {
 159                         tmp = *p;
 160                         *p = 0;
 161                         inode->i_blocks -= blocks;
 162                         inode->i_dirt = 1;
 163                         ext2_free_blocks (inode->i_sb, tmp, 1);
 164                 }
 165         if (IS_SYNC(inode) && ind_bh->b_dirt) {
 166                 ll_rw_block (WRITE, 1, &ind_bh);
 167                 wait_on_buffer (ind_bh);
 168         }
 169         brelse (ind_bh);
 170         return retry;
 171 }
 172 
 173 static int trunc_dindirect (struct inode * inode, int offset,
     /* [previous][next][first][last][top][bottom][index][help] */
 174                             unsigned long * p)
 175 {
 176         int i, tmp;
 177         struct buffer_head * dind_bh;
 178         unsigned long * dind;
 179         int retry = 0;
 180         int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 181         int blocks = inode->i_sb->s_blocksize / 512;
 182 #define DINDIRECT_BLOCK (((int)DIRECT_BLOCK - offset) / addr_per_block)
 183         int dindirect_block = DINDIRECT_BLOCK;
 184 
 185         tmp = *p;
 186         if (!tmp)
 187                 return 0;
 188         dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
 189         if (tmp != *p) {
 190                 brelse (dind_bh);
 191                 return 1;
 192         }
 193         if (!dind_bh) {
 194                 *p = 0;
 195                 return 0;
 196         }
 197 repeat:
 198         for (i = dindirect_block ; i < addr_per_block ; i++) {
 199                 if (i < 0)
 200                         i = 0;
 201                 if (i < dindirect_block)
 202                         goto repeat;
 203                 dind = i + (unsigned long *) dind_bh->b_data;
 204                 tmp = *dind;
 205                 if (!tmp)
 206                         continue;
 207                 retry |= trunc_indirect (inode, offset + (i * addr_per_block),
 208                                           dind);
 209                 dind_bh->b_dirt = 1;
 210         }
 211         dind = (unsigned long *) dind_bh->b_data;
 212         for (i = 0; i < addr_per_block; i++)
 213                 if (*(dind++))
 214                         break;
 215         if (i >= addr_per_block)
 216                 if (dind_bh->b_count != 1)
 217                         retry = 1;
 218                 else {
 219                         tmp = *p;
 220                         *p = 0;
 221                         inode->i_blocks -= blocks;
 222                         inode->i_dirt = 1;
 223                         ext2_free_blocks (inode->i_sb, tmp, 1);
 224                 }
 225         if (IS_SYNC(inode) && dind_bh->b_dirt) {
 226                 ll_rw_block (WRITE, 1, &dind_bh);
 227                 wait_on_buffer (dind_bh);
 228         }
 229         brelse (dind_bh);
 230         return retry;
 231 }
 232 
 233 static int trunc_tindirect (struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 234 {
 235         int i, tmp;
 236         struct buffer_head * tind_bh;
 237         unsigned long * tind, * p;
 238         int retry = 0;
 239         int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 240         int blocks = inode->i_sb->s_blocksize / 512;
 241 #define TINDIRECT_BLOCK (((int)DIRECT_BLOCK - (addr_per_block * addr_per_block + \
 242                           addr_per_block + EXT2_NDIR_BLOCKS)) / \
 243                           (addr_per_block * addr_per_block))
 244         int tindirect_block = TINDIRECT_BLOCK;
 245 
 246         p = inode->u.ext2_i.i_data + EXT2_TIND_BLOCK;
 247         if (!(tmp = *p))
 248                 return 0;
 249         tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
 250         if (tmp != *p) {
 251                 brelse (tind_bh);
 252                 return 1;
 253         }
 254         if (!tind_bh) {
 255                 *p = 0;
 256                 return 0;
 257         }
 258 repeat:
 259         for (i = tindirect_block ; i < addr_per_block ; i++) {
 260                 if (i < 0)
 261                         i = 0;
 262                 if (i < tindirect_block)
 263                         goto repeat;
 264                 tind = i + (unsigned long *) tind_bh->b_data;
 265                 retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS +
 266                         addr_per_block + (i + 1) * addr_per_block * addr_per_block,
 267                         tind);
 268                 tind_bh->b_dirt = 1;
 269         }
 270         tind = (unsigned long *) tind_bh->b_data;
 271         for (i = 0; i < addr_per_block; i++)
 272                 if (*(tind++))
 273                         break;
 274         if (i >= addr_per_block)
 275                 if (tind_bh->b_count != 1)
 276                         retry = 1;
 277                 else {
 278                         tmp = *p;
 279                         *p = 0;
 280                         inode->i_blocks -= blocks;
 281                         inode->i_dirt = 1;
 282                         ext2_free_blocks (inode->i_sb, tmp, 1);
 283                 }
 284         if (IS_SYNC(inode) && tind_bh->b_dirt) {
 285                 ll_rw_block (WRITE, 1, &tind_bh);
 286                 wait_on_buffer (tind_bh);
 287         }
 288         brelse (tind_bh);
 289         return retry;
 290 }
 291                 
 292 void ext2_truncate (struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 293 {
 294         int retry;
 295 
 296         if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
 297             S_ISLNK(inode->i_mode)))
 298                 return;
 299         ext2_discard_prealloc(inode);
 300         while (1) {
 301                 retry = trunc_direct(inode);
 302                 retry |= trunc_indirect (inode, EXT2_IND_BLOCK,
 303                         (unsigned long *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]);
 304                 retry |= trunc_dindirect (inode, EXT2_IND_BLOCK +
 305                         EXT2_ADDR_PER_BLOCK(inode->i_sb),
 306                         (unsigned long *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]);
 307                 retry |= trunc_tindirect (inode);
 308                 if (!retry)
 309                         break;
 310                 if (IS_SYNC(inode) && inode->i_dirt)
 311                         ext2_sync_inode (inode);
 312                 current->counter = 0;
 313                 schedule ();
 314         }
 315         inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 316         inode->i_dirt = 1;
 317 }

/* [previous][next][first][last][top][bottom][index][help] */