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

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