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

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