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

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