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                 ind = ((sysv_zone_t *) indbh->b_data) + i;
 175                 block = tmp = *ind;
 176                 if (sb->sv_convert)
 177                         block = from_coh_ulong(block);
 178                 if (!block)
 179                         continue;
 180                 retry |= trunc_indirect(inode,offset+(i<<sb->sv_ind_per_block_bits),ind,sb->sv_convert,&indbh->b_dirt);
 181         }
 182         for (i = 0; i < sb->sv_ind_per_block; i++)
 183                 if (((sysv_zone_t *) indbh->b_data)[i])
 184                         goto done;
 185         if ((indbh->b_count != 1) || (indtmp != *p)) {
 186                 brelse(indbh);
 187                 return 1;
 188         }
 189         *p = 0;
 190         *dirt = 1;
 191         sysv_free_block(sb,indblock);
 192 done:
 193         brelse(indbh);
 194         return retry;
 195 }
 196 
 197 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] */
 198 {
 199         unsigned long indtmp, indblock;
 200         struct super_block * sb;
 201         struct buffer_head * indbh;
 202         unsigned int i;
 203         sysv_zone_t * ind;
 204         unsigned long tmp, block;
 205         int retry = 0;
 206 
 207         indblock = indtmp = *p;
 208         if (convert)
 209                 indblock = from_coh_ulong(indblock);
 210         if (!indblock)
 211                 return 0;
 212         sb = inode->i_sb;
 213         indbh = sv_bread(sb, inode->i_dev, indblock);
 214         if (indtmp != *p) {
 215                 brelse(indbh);
 216                 return 1;
 217         }
 218         if (!indbh) {
 219                 *p = 0;
 220                 *dirt = 1;
 221                 return 0;
 222         }
 223         if (inode->i_size < offset)
 224                 i = 0;
 225         else
 226                 i = (inode->i_size - offset + sb->sv_ind_per_block_2_block_size_1) >> sb->sv_ind_per_block_2_block_size_bits;
 227         for (; i < sb->sv_ind_per_block; i++) {
 228                 ind = ((sysv_zone_t *) indbh->b_data) + i;
 229                 block = tmp = *ind;
 230                 if (sb->sv_convert)
 231                         block = from_coh_ulong(block);
 232                 if (!block)
 233                         continue;
 234                 retry |= trunc_dindirect(inode,offset+(i<<sb->sv_ind_per_block_2_bits),ind,sb->sv_convert,&indbh->b_dirt);
 235         }
 236         for (i = 0; i < sb->sv_ind_per_block; i++)
 237                 if (((sysv_zone_t *) indbh->b_data)[i])
 238                         goto done;
 239         if ((indbh->b_count != 1) || (indtmp != *p)) {
 240                 brelse(indbh);
 241                 return 1;
 242         }
 243         *p = 0;
 244         *dirt = 1;
 245         sysv_free_block(sb,indblock);
 246 done:
 247         brelse(indbh);
 248         return retry;
 249 }
 250 
 251 static int trunc_all(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 252 {
 253         struct super_block * sb;
 254 
 255         sb = inode->i_sb;
 256         return trunc_direct(inode)
 257              | trunc_indirect(inode,sb->sv_ind0_size,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt)
 258              | trunc_dindirect(inode,sb->sv_ind1_size,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt)
 259              | trunc_tindirect(inode,sb->sv_ind2_size,&inode->u.sysv_i.i_data[12],0,&inode->i_dirt);
 260 }
 261 
 262 
 263 void sysv_truncate(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 264 {
 265         /* If this is called from sysv_put_inode, we needn't worry about
 266          * races as we are just losing the last reference to the inode.
 267          * If this is called from another place, let's hope it's a regular
 268          * file.
 269          * Truncating symbolic links is strange. We assume we don't truncate
 270          * a directory we are just modifying. We ensure we don't truncate
 271          * a regular file we are just writing to, by use of a lock.
 272          */
 273         if (S_ISLNK(inode->i_mode))
 274                 printk("sysv_truncate: truncating symbolic link\n");
 275         else if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
 276                 return;
 277         while (trunc_all(inode)) {
 278                 current->counter = 0;
 279                 schedule();
 280         }
 281         inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 282         inode->i_dirt = 1;
 283 }

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