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

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