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 = get_hash_table (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 = get_hash_table (inode->i_dev, tmp,
 104                                      inode->i_sb->s_blocksize);
 105                 if (i < indirect_block) {
 106                         brelse (bh);
 107                         goto repeat;
 108                 }
 109                 if ((bh && bh->b_count != 1) || tmp != *ind) {
 110                         retry = 1;
 111                         brelse (bh);
 112                         continue;
 113                 }
 114                 *ind = 0;
 115                 ind_bh->b_dirt = 1;
 116                 brelse (bh);
 117                 ext2_free_block (inode->i_sb, tmp);
 118                 inode->i_blocks -= blocks;
 119                 inode->i_dirt = 1;
 120         }
 121         ind = (unsigned long *) ind_bh->b_data;
 122         for (i = 0; i < addr_per_block; i++)
 123                 if (*(ind++))
 124                         break;
 125         if (i >= addr_per_block)
 126                 if (ind_bh->b_count != 1)
 127                         retry = 1;
 128                 else {
 129                         tmp = *p;
 130                         *p = 0;
 131                         inode->i_blocks -= blocks;
 132                         inode->i_dirt = 1;
 133                         ext2_free_block (inode->i_sb, tmp);
 134                 }
 135         brelse (ind_bh);
 136         return retry;
 137 }
 138                 
 139 static int trunc_dindirect (struct inode * inode, int offset,
     /* [previous][next][first][last][top][bottom][index][help] */
 140                             unsigned long * p)
 141 {
 142         int i, tmp;
 143         struct buffer_head * dind_bh;
 144         unsigned long * dind;
 145         int retry = 0;
 146         int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 147         int blocks = inode->i_sb->s_blocksize / 512;
 148 #define DINDIRECT_BLOCK (((int)DIRECT_BLOCK - offset) / addr_per_block)
 149         int dindirect_block = DINDIRECT_BLOCK;
 150 
 151         tmp = *p;
 152         if (!tmp)
 153                 return 0;
 154         dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
 155         if (tmp != *p) {
 156                 brelse (dind_bh);
 157                 return 1;
 158         }
 159         if (!dind_bh) {
 160                 *p = 0;
 161                 return 0;
 162         }
 163 repeat:
 164         for (i = dindirect_block ; i < addr_per_block ; i++) {
 165                 if (i < 0)
 166                         i = 0;
 167                 if (i < dindirect_block)
 168                         goto repeat;
 169                 dind = i + (unsigned long *) dind_bh->b_data;
 170                 tmp = *dind;
 171                 if (!tmp)
 172                         continue;
 173                 retry |= trunc_indirect (inode, offset + (i * addr_per_block),
 174                                           dind);
 175                 dind_bh->b_dirt = 1;
 176         }
 177         dind = (unsigned long *) dind_bh->b_data;
 178         for (i = 0; i < addr_per_block; i++)
 179                 if (*(dind++))
 180                         break;
 181         if (i >= addr_per_block)
 182                 if (dind_bh->b_count != 1)
 183                         retry = 1;
 184                 else {
 185                         tmp = *p;
 186                         *p = 0;
 187                         inode->i_blocks -= blocks;
 188                         inode->i_dirt = 1;
 189                         ext2_free_block (inode->i_sb, tmp);
 190                 }
 191         brelse (dind_bh);
 192         return retry;
 193 }
 194 
 195 static int trunc_tindirect (struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 196 {
 197         int i, tmp;
 198         struct buffer_head * tind_bh;
 199         unsigned long * tind, * p;
 200         int retry = 0;
 201         int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 202         int blocks = inode->i_sb->s_blocksize / 512;
 203 #define TINDIRECT_BLOCK (((int)DIRECT_BLOCK - (addr_per_block * addr_per_block + \
 204                           addr_per_block + EXT2_NDIR_BLOCKS)) / \
 205                           (addr_per_block * addr_per_block))
 206         int tindirect_block = TINDIRECT_BLOCK;
 207 
 208         p = inode->u.ext2_i.i_data + EXT2_TIND_BLOCK;
 209         if (!(tmp = *p))
 210                 return 0;
 211         tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
 212         if (tmp != *p) {
 213                 brelse (tind_bh);
 214                 return 1;
 215         }
 216         if (!tind_bh) {
 217                 *p = 0;
 218                 return 0;
 219         }
 220 repeat:
 221         for (i = tindirect_block ; i < addr_per_block ; i++) {
 222                 if (i < 0)
 223                         i = 0;
 224                 if (i < tindirect_block)
 225                         goto repeat;
 226                 tind = i + (unsigned long *) tind_bh->b_data;
 227                 retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS +
 228 /*                      addr_per_block + addr_per_block * addr_per_block +
 229                         (i * (addr_per_block * addr_per_block)), tind); */
 230                         addr_per_block + (i + 1) * addr_per_block * addr_per_block,
 231                         tind);
 232                 tind_bh->b_dirt = 1;
 233         }
 234         tind = (unsigned long *) tind_bh->b_data;
 235         for (i = 0; i < addr_per_block; i++)
 236                 if (*(tind++))
 237                         break;
 238         if (i >= addr_per_block)
 239                 if (tind_bh->b_count != 1)
 240                         retry = 1;
 241                 else {
 242                         tmp = *p;
 243                         *p = 0;
 244                         inode->i_blocks -= blocks;
 245                         inode->i_dirt = 1;
 246                         ext2_free_block (inode->i_sb, tmp);
 247                 }
 248         brelse (tind_bh);
 249         return retry;
 250 }
 251                 
 252 void ext2_truncate (struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 253 {
 254         int retry;
 255 
 256         if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
 257             S_ISLNK(inode->i_mode)))
 258                 return;
 259         while (1) {
 260                 retry = trunc_direct(inode);
 261                 retry |= trunc_indirect (inode, EXT2_IND_BLOCK,
 262                         (unsigned long *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]);
 263                 retry |= trunc_dindirect (inode, EXT2_IND_BLOCK +
 264                         EXT2_ADDR_PER_BLOCK(inode->i_sb),
 265                         (unsigned long *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]);
 266                 retry |= trunc_tindirect (inode);
 267                 if (!retry)
 268                         break;
 269                 current->counter = 0;
 270                 schedule ();
 271         }
 272         inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 273         inode->i_dirt = 1;
 274 }
 275 
 276 /*
 277  * Called when a inode is released. Note that this is different
 278  * from ext2_open: open gets called at every open, but release
 279  * gets called only when /all/ the files are closed.
 280  */
 281 void ext2_release (struct inode * inode, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 282 {
 283         printk ("ext2_release not implemented\n");
 284 }

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