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
  6. ext2_release

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

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