home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.2 / LINUX-1.2 / LINUX-1 / linux / fs / sysv / truncate.c < prev   
Encoding:
C/C++ Source or Header  |  1995-01-23  |  7.0 KB  |  288 lines

  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)
  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)
  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)
  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)
  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)
  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)
  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. }
  288.