home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.0 / LINUX-1.0 / LINUX-1 / linux / fs / sysv / truncate.c < prev   
Encoding:
C/C++ Source or Header  |  1993-07-21  |  12.4 KB  |  509 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. #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 noone 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)
  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)
  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.         bh->b_dirt = 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)
  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)
  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)
  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)
  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)
  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.         indbh->b_dirt = 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)
  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)
  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)
  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)
  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. }
  509.