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

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