root/fs/sysv/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. trunc_all
  6. sysv_truncate

   1 /*
   2  *  linux/fs/sysv/truncate.c
   3  *
   4  *  minix/truncate.c
   5  *  Copyright (C) 1991, 1992  Linus Torvalds
   6  *
   7  *  coh/truncate.c
   8  *  Copyright (C) 1993  Pascal Haible, Bruno Haible
   9  *
  10  *  sysv/truncate.c
  11  *  Copyright (C) 1993  Bruno Haible
  12  */
  13 
  14 #include <linux/sched.h>
  15 #include <linux/fs.h>
  16 #include <linux/sysv_fs.h>
  17 #include <linux/stat.h>
  18 
  19 
  20 /* Linus' implementation of truncate.
  21  * It doesn't need locking because it can tell from looking at bh->b_count
  22  * whether a given block is in use elsewhere.
  23  */
  24 
  25 /*
  26  * Truncate has the most races in the whole filesystem: coding it is
  27  * a pain in the a**. Especially as I don't do any locking...
  28  *
  29  * The code may look a bit weird, but that's just because I've tried to
  30  * handle things like file-size changes in a somewhat graceful manner.
  31  * Anyway, truncating a file at the same time somebody else writes to it
  32  * is likely to result in pretty weird behaviour...
  33  *
  34  * The new code handles normal truncates (size = 0) as well as the more
  35  * general case (size = XXX). I hope.
  36  */
  37 
  38 /* We throw away any data beyond inode->i_size. */
  39 
  40 static int trunc_direct(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
  41 {
  42         struct super_block * sb;
  43         unsigned int i;
  44         unsigned long * p;
  45         unsigned long block;
  46         struct buffer_head * bh;
  47         int retry = 0;
  48 
  49         sb = inode->i_sb;
  50 repeat:
  51         for (i = ((unsigned long) inode->i_size + sb->sv_block_size_1) >> sb->sv_block_size_bits; i < 10; i++) {
  52                 p = inode->u.sysv_i.i_data + i;
  53                 block = *p;
  54                 if (!block)
  55                         continue;
  56                 bh = sv_get_hash_table(sb, inode->i_dev, block);
  57                 if ((i << sb->sv_block_size_bits) < inode->i_size) {
  58                         brelse(bh);
  59                         goto repeat;
  60                 }
  61                 if ((bh && bh->b_count != 1) || (block != *p)) {
  62                         retry = 1;
  63                         brelse(bh);
  64                         continue;
  65                 }
  66                 *p = 0;
  67                 inode->i_dirt = 1;
  68                 brelse(bh);
  69                 sysv_free_block(sb,block);
  70         }
  71         return retry;
  72 }
  73 
  74 static int trunc_indirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
     /* [previous][next][first][last][top][bottom][index][help] */
  75 {
  76         unsigned long indtmp, indblock;
  77         struct super_block * sb;
  78         struct buffer_head * indbh;
  79         unsigned int i;
  80         sysv_zone_t * ind;
  81         unsigned long tmp, block;
  82         struct buffer_head * bh;
  83         int retry = 0;
  84 
  85         indblock = indtmp = *p;
  86         if (convert)
  87                 indblock = from_coh_ulong(indblock);
  88         if (!indblock)
  89                 return 0;
  90         sb = inode->i_sb;
  91         indbh = sv_bread(sb, inode->i_dev, indblock);
  92         if (indtmp != *p) {
  93                 brelse(indbh);
  94                 return 1;
  95         }
  96         if (!indbh) {
  97                 *p = 0;
  98                 *dirt = 1;
  99                 return 0;
 100         }
 101 repeat:
 102         if (inode->i_size < offset)
 103                 i = 0;
 104         else
 105                 i = (inode->i_size - offset + sb->sv_block_size_1) >> sb->sv_block_size_bits;
 106         for (; i < sb->sv_ind_per_block; i++) {
 107                 ind = ((sysv_zone_t *) indbh->b_data) + i;
 108                 block = tmp = *ind;
 109                 if (sb->sv_convert)
 110                         block = from_coh_ulong(block);
 111                 if (!block)
 112                         continue;
 113                 bh = sv_get_hash_table(sb, inode->i_dev, block);
 114                 if ((i << sb->sv_block_size_bits) + offset < inode->i_size) {
 115                         brelse(bh);
 116                         goto repeat;
 117                 }
 118                 if ((bh && bh->b_count != 1) || (tmp != *ind)) {
 119                         retry = 1;
 120                         brelse(bh);
 121                         continue;
 122                 }
 123                 *ind = 0;
 124                 mark_buffer_dirty(indbh, 1);
 125                 brelse(bh);
 126                 sysv_free_block(sb,block);
 127         }
 128         for (i = 0; i < sb->sv_ind_per_block; i++)
 129                 if (((sysv_zone_t *) indbh->b_data)[i])
 130                         goto done;
 131         if ((indbh->b_count != 1) || (indtmp != *p)) {
 132                 brelse(indbh);
 133                 return 1;
 134         }
 135         *p = 0;
 136         *dirt = 1;
 137         sysv_free_block(sb,indblock);
 138 done:
 139         brelse(indbh);
 140         return retry;
 141 }
 142 
 143 static int trunc_dindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
     /* [previous][next][first][last][top][bottom][index][help] */
 144 {
 145         unsigned long indtmp, indblock;
 146         struct super_block * sb;
 147         struct buffer_head * indbh;
 148         unsigned int i;
 149         sysv_zone_t * ind;
 150         unsigned long tmp, block;
 151         int retry = 0;
 152 
 153         indblock = indtmp = *p;
 154         if (convert)
 155                 indblock = from_coh_ulong(indblock);
 156         if (!indblock)
 157                 return 0;
 158         sb = inode->i_sb;
 159         indbh = sv_bread(sb, inode->i_dev, indblock);
 160         if (indtmp != *p) {
 161                 brelse(indbh);
 162                 return 1;
 163         }
 164         if (!indbh) {
 165                 *p = 0;
 166                 *dirt = 1;
 167                 return 0;
 168         }
 169         if (inode->i_size < offset)
 170                 i = 0;
 171         else
 172                 i = (inode->i_size - offset + sb->sv_ind_per_block_block_size_1) >> sb->sv_ind_per_block_block_size_bits;
 173         for (; i < sb->sv_ind_per_block; i++) {
 174                 unsigned char dirty = 0;
 175                 ind = ((sysv_zone_t *) indbh->b_data) + i;
 176                 block = tmp = *ind;
 177                 if (sb->sv_convert)
 178                         block = from_coh_ulong(block);
 179                 if (!block)
 180                         continue;
 181                 retry |= trunc_indirect(inode,offset+(i<<sb->sv_ind_per_block_bits),ind,sb->sv_convert,&dirty);
 182                 if (dirty)
 183                         mark_buffer_dirty(indbh, 1);
 184         }
 185         for (i = 0; i < sb->sv_ind_per_block; i++)
 186                 if (((sysv_zone_t *) indbh->b_data)[i])
 187                         goto done;
 188         if ((indbh->b_count != 1) || (indtmp != *p)) {
 189                 brelse(indbh);
 190                 return 1;
 191         }
 192         *p = 0;
 193         *dirt = 1;
 194         sysv_free_block(sb,indblock);
 195 done:
 196         brelse(indbh);
 197         return retry;
 198 }
 199 
 200 static int trunc_tindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
     /* [previous][next][first][last][top][bottom][index][help] */
 201 {
 202         unsigned long indtmp, indblock;
 203         struct super_block * sb;
 204         struct buffer_head * indbh;
 205         unsigned int i;
 206         sysv_zone_t * ind;
 207         unsigned long tmp, block;
 208         int retry = 0;
 209 
 210         indblock = indtmp = *p;
 211         if (convert)
 212                 indblock = from_coh_ulong(indblock);
 213         if (!indblock)
 214                 return 0;
 215         sb = inode->i_sb;
 216         indbh = sv_bread(sb, inode->i_dev, indblock);
 217         if (indtmp != *p) {
 218                 brelse(indbh);
 219                 return 1;
 220         }
 221         if (!indbh) {
 222                 *p = 0;
 223                 *dirt = 1;
 224                 return 0;
 225         }
 226         if (inode->i_size < offset)
 227                 i = 0;
 228         else
 229                 i = (inode->i_size - offset + sb->sv_ind_per_block_2_block_size_1) >> sb->sv_ind_per_block_2_block_size_bits;
 230         for (; i < sb->sv_ind_per_block; i++) {
 231                 unsigned char dirty = 0;
 232                 ind = ((sysv_zone_t *) indbh->b_data) + i;
 233                 block = tmp = *ind;
 234                 if (sb->sv_convert)
 235                         block = from_coh_ulong(block);
 236                 if (!block)
 237                         continue;
 238                 retry |= trunc_dindirect(inode,offset+(i<<sb->sv_ind_per_block_2_bits),ind,sb->sv_convert,&dirty);
 239                 if (dirty)
 240                         mark_buffer_dirty(indbh, 1);
 241         }
 242         for (i = 0; i < sb->sv_ind_per_block; i++)
 243                 if (((sysv_zone_t *) indbh->b_data)[i])
 244                         goto done;
 245         if ((indbh->b_count != 1) || (indtmp != *p)) {
 246                 brelse(indbh);
 247                 return 1;
 248         }
 249         *p = 0;
 250         *dirt = 1;
 251         sysv_free_block(sb,indblock);
 252 done:
 253         brelse(indbh);
 254         return retry;
 255 }
 256 
 257 static int trunc_all(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 258 {
 259         struct super_block * sb;
 260 
 261         sb = inode->i_sb;
 262         return trunc_direct(inode)
 263              | trunc_indirect(inode,sb->sv_ind0_size,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt)
 264              | trunc_dindirect(inode,sb->sv_ind1_size,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt)
 265              | trunc_tindirect(inode,sb->sv_ind2_size,&inode->u.sysv_i.i_data[12],0,&inode->i_dirt);
 266 }
 267 
 268 
 269 void sysv_truncate(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 270 {
 271         /* If this is called from sysv_put_inode, we needn't worry about
 272          * races as we are just losing the last reference to the inode.
 273          * If this is called from another place, let's hope it's a regular
 274          * file.
 275          * Truncating symbolic links is strange. We assume we don't truncate
 276          * a directory we are just modifying. We ensure we don't truncate
 277          * a regular file we are just writing to, by use of a lock.
 278          */
 279         if (S_ISLNK(inode->i_mode))
 280                 printk("sysv_truncate: truncating symbolic link\n");
 281         else if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
 282                 return;
 283         while (trunc_all(inode)) {
 284                 current->counter = 0;
 285                 schedule();
 286         }
 287         inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 288         inode->i_dirt = 1;
 289 }

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