root/fs/sysv/truncate.c

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

DEFINITIONS

This source file includes following definitions.
  1. coh_trunc_direct
  2. coh_trunc_indirect
  3. coh_trunc_dindirect
  4. coh_trunc_tindirect
  5. coh_trunc_all
  6. trunc_direct
  7. trunc_indirect
  8. trunc_dindirect
  9. trunc_tindirect
  10. trunc_all
  11. 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 /* There are two different implementations of truncate() here.
  21  * One (by Bruno) needs to do locking to ensure that no one is writing
  22  * to a block being truncated away and incorporated into the free list.
  23  * The better one (by Linus) doesn't need locking because it can tell from
  24  * looking at bh->b_count whether a given block is in use elsewhere.
  25  * Alas, this doesn't work if block_size < BLOCK_SIZE.
  26  */
  27 
  28 
  29 /* Bruno's implementation of truncate. */
  30 
  31 /* Leave at most `blocks' direct blocks. */
  32 static int coh_trunc_direct (struct inode * inode, unsigned long blocks)
     /* [previous][next][first][last][top][bottom][index][help] */
  33 {
  34         unsigned int i;
  35         unsigned long * p;
  36         unsigned long block;
  37 
  38         for (i = blocks; i < 10 ; i++) {
  39                 p = &inode->u.sysv_i.i_data[i];
  40                 block = *p;
  41                 if (!block)
  42                         continue;
  43                 *p = 0;
  44                 inode->i_dirt = 1;
  45                 sysv_free_block(inode->i_sb,block);
  46         }
  47         return 0;
  48 }
  49 
  50 /* Leave at most `blocks' blocks out of an indirect block whose number is
  51  * from_coh_ulong(*p) if convert=1, *p if convert=0.
  52  */
  53 static int coh_trunc_indirect (struct inode * inode, unsigned long blocks, unsigned long * p, int convert, unsigned char * dirt)
     /* [previous][next][first][last][top][bottom][index][help] */
  54 {
  55         struct super_block * sb = inode->i_sb;
  56         unsigned long tmp, block, indblock;
  57         struct buffer_head * bh;
  58         char * bh_data;
  59         unsigned long i;
  60         sysv_zone_t * ind;
  61 
  62         if (blocks >= sb->sv_ind_per_block)
  63                 return 0;
  64         block = tmp = *p;
  65         if (convert)
  66                 block = from_coh_ulong(block);
  67         if (!block)
  68                 return 0;
  69         bh = sysv_bread(sb,inode->i_dev,block,&bh_data);
  70         if (tmp != *p) {
  71                 brelse(bh);
  72                 return 1;
  73         }
  74         if (!bh) {
  75                 *p = 0;
  76                 *dirt = 1;
  77                 return 0;
  78         }
  79         for (i = blocks; i < sb->sv_ind_per_block; i++) {
  80                 ind = &((sysv_zone_t *) bh_data)[i];
  81                 indblock = *ind;
  82                 if (sb->sv_convert)
  83                         indblock = from_coh_ulong(indblock);
  84                 if (!indblock)
  85                         continue;
  86                 *ind = 0;
  87                 mark_buffer_dirty(bh, 1);
  88                 sysv_free_block(sb,indblock);
  89         }
  90         for (i = 0; i < sb->sv_ind_per_block; i++)
  91                 if (((sysv_zone_t *) bh_data)[i])
  92                         goto done;
  93         if (tmp != *p) {
  94                 brelse(bh);
  95                 return 1;
  96         }
  97         *p = 0;
  98         *dirt = 1;
  99         sysv_free_block(sb,block);
 100 done:
 101         brelse(bh);
 102         return 0;
 103 }
 104 
 105 /* Leave at most `blocks' blocks out of an double indirect block whose number is
 106  * from_coh_ulong(*p) if convert=1, *p if convert=0.
 107  */
 108 static int coh_trunc_dindirect (struct inode * inode, unsigned long blocks, unsigned long * p, int convert, unsigned char * dirt)
     /* [previous][next][first][last][top][bottom][index][help] */
 109 {
 110         struct super_block * sb = inode->i_sb;
 111         unsigned long tmp, block, dindblock;
 112         struct buffer_head * bh;
 113         char * bh_data;
 114         unsigned long i, j;
 115         sysv_zone_t * dind;
 116         int retry = 0;
 117 
 118         if (blocks >= sb->sv_ind_per_block_2)
 119                 return 0;
 120         block = tmp = *p;
 121         if (convert)
 122                 block = from_coh_ulong(block);
 123         if (!block)
 124                 return 0;
 125         bh = sysv_bread(sb,inode->i_dev,block,&bh_data);
 126         if (tmp != *p) {
 127                 brelse(bh);
 128                 return 1;
 129         }
 130         if (!bh) {
 131                 *p = 0;
 132                 *dirt = 1;
 133                 return 0;
 134         }
 135         for (i = blocks >> sb->sv_ind_per_block_bits, j = blocks & sb->sv_ind_per_block_1;
 136              i < sb->sv_ind_per_block;
 137              i++, j = 0) {
 138                 /* j = max(blocks-i*ind_per_block,0) */
 139                 dind = &((sysv_zone_t *) bh_data)[i];
 140                 dindblock = *dind;
 141                 if (sb->sv_convert)
 142                         dindblock = from_coh_ulong(dindblock);
 143                 if (!dindblock)
 144                         continue;
 145                 retry |= coh_trunc_indirect(inode,j,dind,sb->sv_convert,&bh->b_dirt);
 146         }
 147         for (i = 0; i < sb->sv_ind_per_block; i++)
 148                 if (((sysv_zone_t *) bh_data)[i])
 149                         goto done;
 150         if (tmp != *p) {
 151                 brelse(bh);
 152                 return 1;
 153         }
 154         *p = 0;
 155         *dirt = 1;
 156         sysv_free_block(sb,block);
 157 done:
 158         brelse(bh);
 159         return retry;
 160 }
 161 
 162 /* Leave at most `blocks' blocks out of an triple indirect block whose number is
 163  * from_coh_ulong(*p) if convert=1, *p if convert=0.
 164  */
 165 static int coh_trunc_tindirect (struct inode * inode, unsigned long blocks, unsigned long * p)
     /* [previous][next][first][last][top][bottom][index][help] */
 166 {
 167         struct super_block * sb = inode->i_sb;
 168         unsigned long block, tindblock;
 169         struct buffer_head * bh;
 170         char * bh_data;
 171         unsigned long i, j;
 172         sysv_zone_t * tind;
 173         int retry = 0;
 174 
 175         if (blocks >= sb->sv_ind_per_block_3)
 176                 return 0;
 177         block = *p;
 178         if (!block)
 179                 return 0;
 180         bh = sysv_bread(sb,inode->i_dev,block,&bh_data);
 181         if (block != *p) {
 182                 brelse(bh);
 183                 return 1;
 184         }
 185         if (!bh) {
 186                 *p = 0;
 187                 inode->i_dirt = 1;
 188                 return 0;
 189         }
 190         for (i = blocks >> sb->sv_ind_per_block_2_bits, j = blocks & sb->sv_ind_per_block_2_1;
 191              i < sb->sv_ind_per_block;
 192              i++, j = 0) {
 193                 /* j = max(blocks-i*ind_per_block^2,0) */
 194                 tind = &((sysv_zone_t *) bh_data)[i];
 195                 tindblock = *tind;
 196                 if (sb->sv_convert)
 197                         tindblock = from_coh_ulong(tindblock);
 198                 if (!tindblock)
 199                         continue;
 200                 retry |= coh_trunc_dindirect(inode,j,tind,sb->sv_convert,&bh->b_dirt);
 201         }
 202         for (i = 0; i < sb->sv_ind_per_block; i++)
 203                 if (((sysv_zone_t *) bh_data)[i])
 204                         goto done;
 205         if (block != *p) {
 206                 brelse(bh);
 207                 return 1;
 208         }
 209         *p = 0;
 210         inode->i_dirt = 1;
 211         sysv_free_block(sb,block);
 212 done:
 213         brelse(bh);
 214         return retry;
 215 }
 216 
 217 static int coh_trunc_all(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 218 {
 219         struct super_block * sb = inode->i_sb;
 220         long blocks;
 221         int retry;
 222 
 223         blocks = (inode->i_size + sb->sv_block_size_1) >> sb->sv_block_size_bits;
 224         retry = coh_trunc_direct(inode,blocks);
 225         blocks -= 10;
 226         if (blocks < 0) blocks = 0;
 227         retry |= coh_trunc_indirect(inode,blocks,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt);
 228         blocks -= sb->sv_ind_per_block;
 229         if (blocks < 0) blocks = 0;
 230         retry |= coh_trunc_dindirect(inode,blocks,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt);
 231         blocks -= sb->sv_ind_per_block_2;
 232         if (blocks < 0) blocks = 0;
 233         retry |= coh_trunc_tindirect(inode,blocks,&inode->u.sysv_i.i_data[12]);
 234         return retry;
 235 }
 236 
 237 
 238 /* Linus' implementation of truncate. Used only if block_size = BLOCK_SIZE. */
 239 
 240 /*
 241  * Truncate has the most races in the whole filesystem: coding it is
 242  * a pain in the a**. Especially as I don't do any locking...
 243  *
 244  * The code may look a bit weird, but that's just because I've tried to
 245  * handle things like file-size changes in a somewhat graceful manner.
 246  * Anyway, truncating a file at the same time somebody else writes to it
 247  * is likely to result in pretty weird behaviour...
 248  *
 249  * The new code handles normal truncates (size = 0) as well as the more
 250  * general case (size = XXX). I hope.
 251  */
 252 
 253 /* We throw away any data beyond inode->i_size. */
 254 
 255 static int trunc_direct(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 256 {
 257         struct super_block * sb;
 258         unsigned int i;
 259         unsigned long * p;
 260         unsigned long block;
 261         struct buffer_head * bh;
 262         int retry = 0;
 263 
 264         sb = inode->i_sb;
 265 repeat:
 266         for (i = ((unsigned long) inode->i_size + BLOCK_SIZE-1) / BLOCK_SIZE; i < 10; i++) {
 267                 p = inode->u.sysv_i.i_data + i;
 268                 block = *p;
 269                 if (!block)
 270                         continue;
 271                 bh = get_hash_table(inode->i_dev,block+sb->sv_block_base,BLOCK_SIZE);
 272                 if (i*BLOCK_SIZE < inode->i_size) {
 273                         brelse(bh);
 274                         goto repeat;
 275                 }
 276                 if ((bh && bh->b_count != 1) || (block != *p)) {
 277                         retry = 1;
 278                         brelse(bh);
 279                         continue;
 280                 }
 281                 *p = 0;
 282                 inode->i_dirt = 1;
 283                 brelse(bh);
 284                 sysv_free_block(sb,block);
 285         }
 286         return retry;
 287 }
 288 
 289 #define IND_PER_BLOCK   (BLOCK_SIZE / sizeof(sysv_zone_t))
 290 
 291 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] */
 292 {
 293         unsigned long indtmp, indblock;
 294         struct super_block * sb;
 295         struct buffer_head * indbh;
 296         unsigned int i;
 297         sysv_zone_t * ind;
 298         unsigned long tmp, block;
 299         struct buffer_head * bh;
 300         int retry = 0;
 301 
 302         indblock = indtmp = *p;
 303         if (convert)
 304                 indblock = from_coh_ulong(indblock);
 305         if (!indblock)
 306                 return 0;
 307         sb = inode->i_sb;
 308         indbh = bread(inode->i_dev,indblock+sb->sv_block_base,BLOCK_SIZE);
 309         if (indtmp != *p) {
 310                 brelse(indbh);
 311                 return 1;
 312         }
 313         if (!indbh) {
 314                 *p = 0;
 315                 *dirt = 1;
 316                 return 0;
 317         }
 318 repeat:
 319         if (inode->i_size < offset)
 320                 i = 0;
 321         else
 322                 i = (inode->i_size - offset + BLOCK_SIZE-1) / BLOCK_SIZE;
 323         for (; i < IND_PER_BLOCK; i++) {
 324                 ind = ((sysv_zone_t *) indbh->b_data) + i;
 325                 block = tmp = *ind;
 326                 if (sb->sv_convert)
 327                         block = from_coh_ulong(block);
 328                 if (!block)
 329                         continue;
 330                 bh = get_hash_table(inode->i_dev,block+sb->sv_block_base,BLOCK_SIZE);
 331                 if (i*BLOCK_SIZE + offset < inode->i_size) {
 332                         brelse(bh);
 333                         goto repeat;
 334                 }
 335                 if ((bh && bh->b_count != 1) || (tmp != *ind)) {
 336                         retry = 1;
 337                         brelse(bh);
 338                         continue;
 339                 }
 340                 *ind = 0;
 341                 mark_buffer_dirty(indbh, 1);
 342                 brelse(bh);
 343                 sysv_free_block(sb,block);
 344         }
 345         for (i = 0; i < IND_PER_BLOCK; i++)
 346                 if (((sysv_zone_t *) indbh->b_data)[i])
 347                         goto done;
 348         if ((indbh->b_count != 1) || (indtmp != *p)) {
 349                 brelse(indbh);
 350                 return 1;
 351         }
 352         *p = 0;
 353         *dirt = 1;
 354         sysv_free_block(sb,indblock);
 355 done:
 356         brelse(indbh);
 357         return retry;
 358 }
 359 
 360 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] */
 361 {
 362         unsigned long indtmp, indblock;
 363         struct super_block * sb;
 364         struct buffer_head * indbh;
 365         unsigned int i;
 366         sysv_zone_t * ind;
 367         unsigned long tmp, block;
 368         int retry = 0;
 369 
 370         indblock = indtmp = *p;
 371         if (convert)
 372                 indblock = from_coh_ulong(indblock);
 373         if (!indblock)
 374                 return 0;
 375         sb = inode->i_sb;
 376         indbh = bread(inode->i_dev,indblock+sb->sv_block_base,BLOCK_SIZE);
 377         if (indtmp != *p) {
 378                 brelse(indbh);
 379                 return 1;
 380         }
 381         if (!indbh) {
 382                 *p = 0;
 383                 *dirt = 1;
 384                 return 0;
 385         }
 386         if (inode->i_size < offset)
 387                 i = 0;
 388         else
 389                 i = (inode->i_size - offset + IND_PER_BLOCK*BLOCK_SIZE-1) / (IND_PER_BLOCK*BLOCK_SIZE);
 390         for (; i < IND_PER_BLOCK; i++) {
 391                 ind = ((sysv_zone_t *) indbh->b_data) + i;
 392                 block = tmp = *ind;
 393                 if (sb->sv_convert)
 394                         block = from_coh_ulong(block);
 395                 if (!block)
 396                         continue;
 397                 retry |= trunc_indirect(inode,offset+i*IND_PER_BLOCK,ind,sb->sv_convert,&indbh->b_dirt);
 398         }
 399         for (i = 0; i < IND_PER_BLOCK; i++)
 400                 if (((sysv_zone_t *) indbh->b_data)[i])
 401                         goto done;
 402         if ((indbh->b_count != 1) || (indtmp != *p)) {
 403                 brelse(indbh);
 404                 return 1;
 405         }
 406         *p = 0;
 407         *dirt = 1;
 408         sysv_free_block(sb,indblock);
 409 done:
 410         brelse(indbh);
 411         return retry;
 412 }
 413 
 414 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] */
 415 {
 416         unsigned long indtmp, indblock;
 417         struct super_block * sb;
 418         struct buffer_head * indbh;
 419         unsigned int i;
 420         sysv_zone_t * ind;
 421         unsigned long tmp, block;
 422         int retry = 0;
 423 
 424         indblock = indtmp = *p;
 425         if (convert)
 426                 indblock = from_coh_ulong(indblock);
 427         if (!indblock)
 428                 return 0;
 429         sb = inode->i_sb;
 430         indbh = bread(inode->i_dev,indblock+sb->sv_block_base,BLOCK_SIZE);
 431         if (indtmp != *p) {
 432                 brelse(indbh);
 433                 return 1;
 434         }
 435         if (!indbh) {
 436                 *p = 0;
 437                 *dirt = 1;
 438                 return 0;
 439         }
 440         if (inode->i_size < offset)
 441                 i = 0;
 442         else
 443                 i = (inode->i_size - offset + IND_PER_BLOCK*IND_PER_BLOCK*BLOCK_SIZE-1) / (IND_PER_BLOCK*IND_PER_BLOCK*BLOCK_SIZE);
 444         for (; i < IND_PER_BLOCK; i++) {
 445                 ind = ((sysv_zone_t *) indbh->b_data) + i;
 446                 block = tmp = *ind;
 447                 if (sb->sv_convert)
 448                         block = from_coh_ulong(block);
 449                 if (!block)
 450                         continue;
 451                 retry |= trunc_dindirect(inode,offset+i*IND_PER_BLOCK*IND_PER_BLOCK,ind,sb->sv_convert,&indbh->b_dirt);
 452         }
 453         for (i = 0; i < IND_PER_BLOCK; i++)
 454                 if (((sysv_zone_t *) indbh->b_data)[i])
 455                         goto done;
 456         if ((indbh->b_count != 1) || (indtmp != *p)) {
 457                 brelse(indbh);
 458                 return 1;
 459         }
 460         *p = 0;
 461         *dirt = 1;
 462         sysv_free_block(sb,indblock);
 463 done:
 464         brelse(indbh);
 465         return retry;
 466 }
 467 
 468 static int trunc_all(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 469 {
 470         return trunc_direct(inode)
 471              | trunc_indirect(inode,10*BLOCK_SIZE,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt)
 472              | trunc_dindirect(inode,(10+IND_PER_BLOCK)*BLOCK_SIZE,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt)
 473              | trunc_tindirect(inode,(10+IND_PER_BLOCK+IND_PER_BLOCK*IND_PER_BLOCK)*BLOCK_SIZE,&inode->u.sysv_i.i_data[12],0,&inode->i_dirt);
 474 }
 475 
 476 
 477 void sysv_truncate(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 478 {
 479         /* If this is called from sysv_put_inode, we needn't worry about
 480          * races as we are just losing the last reference to the inode.
 481          * If this is called from another place, let's hope it's a regular
 482          * file.
 483          * Truncating symbolic links is strange. We assume we don't truncate
 484          * a directory we are just modifying. We ensure we don't truncate
 485          * a regular file we are just writing to, by use of a lock.
 486          */
 487         if (S_ISLNK(inode->i_mode))
 488                 printk("sysv_truncate: truncating symbolic link\n");
 489         else if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
 490                 return;
 491         if (inode->i_sb->sv_block_size_ratio_bits > 0) { /* block_size < BLOCK_SIZE ? */
 492                 coh_lock_inode(inode); /* do not write to the inode while we truncate */
 493                 while (coh_trunc_all(inode)) {
 494                         current->counter = 0;
 495                         schedule();
 496                 }
 497                 inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 498                 inode->i_dirt = 1;
 499                 coh_unlock_inode(inode);
 500         } else {
 501                 while (trunc_all(inode)) {
 502                         current->counter = 0;
 503                         schedule();
 504                 }
 505                 inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 506                 inode->i_dirt = 1;
 507         }
 508 }

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