Exemplo n.º 1
0
/*
** We pack the tails of files on file close, not at the time they are written.
** This implies an unnecessary copy of the tail and an unnecessary indirect item
** insertion/balancing, for files that are written in one write.
** It avoids unnecessary tail packings (balances) for files that are written in
** multiple writes and are small enough to have tails.
** 
** file_release is called by the VFS layer when the file is closed.  If
** this is the last open file descriptor, and the file
** small enough to have a tail, and the tail is currently in an
** unformatted node, the tail is converted back into a direct item.
** 
** We use reiserfs_truncate_file to pack the tail, since it already has
** all the conditions coded.  
*/
static int reiserfs_file_release (struct inode * inode, struct file * filp)
{

    struct reiserfs_transaction_handle th ;
    int windex ;

    if (!S_ISREG (inode->i_mode))
	BUG ();

    /* fast out for when nothing needs to be done */
    if ((atomic_read(&inode->i_count) > 1 ||
         !(inode->u.reiserfs_i.i_flags & i_pack_on_close_mask) || 
         !tail_has_to_be_packed(inode))       && 
	inode->u.reiserfs_i.i_prealloc_count <= 0) {
	return 0;
    }    
    
    lock_kernel() ;
    down (&inode->i_sem); 
    journal_begin(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3) ;
    reiserfs_update_inode_transaction(inode) ;

#ifdef REISERFS_PREALLOCATE
    reiserfs_discard_prealloc (&th, inode);
#endif
    journal_end(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3) ;

    if (atomic_read(&inode->i_count) <= 1 &&
	(inode->u.reiserfs_i.i_flags & i_pack_on_close_mask) &&
        tail_has_to_be_packed (inode)) {
	/* if regular file is released by last holder and it has been
	   appended (we append by unformatted node only) or its direct
	   item(s) had to be converted, then it may have to be
	   indirect2direct converted */
	windex = push_journal_writer("file_release") ;
	reiserfs_truncate_file(inode, 0) ;
	pop_journal_writer(windex) ;
    }
    up (&inode->i_sem); 
    unlock_kernel() ;
    return 0;
}
Exemplo n.º 2
0
static int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) {
    struct inode *inode = dentry->d_inode ;
    int error ;
    if (attr->ia_valid & ATTR_SIZE) {
	/* version 2 items will be caught by the s_maxbytes check
	** done for us in vmtruncate
	*/
	if (get_inode_item_key_version(inode) == KEY_FORMAT_3_5 &&
	    attr->ia_size > MAX_NON_LFS)
            return -EFBIG ;

	/* fill in hole pointers in the expanding truncate case. */
        if (attr->ia_size > inode->i_size) {
	    error = generic_cont_expand(inode, attr->ia_size) ;
	    if (inode->u.reiserfs_i.i_prealloc_count > 0) {
		struct reiserfs_transaction_handle th ;
		/* we're changing at most 2 bitmaps, inode + super */
		journal_begin(&th, inode->i_sb, 4) ;
		reiserfs_discard_prealloc (&th, inode);
		journal_end(&th, inode->i_sb, 4) ;
	    }
	    if (error)
	        return error ;
	}
    }

    if ((((attr->ia_valid & ATTR_UID) && (attr->ia_uid & ~0xffff)) ||
	 ((attr->ia_valid & ATTR_GID) && (attr->ia_gid & ~0xffff))) &&
	(get_inode_sd_version (inode) == STAT_DATA_V1))
		/* stat data of format v3.5 has 16 bit uid and gid */
	    return -EINVAL;

    error = inode_change_ok(inode, attr) ;
    if (!error)
        inode_setattr(inode, attr) ;

    return error ;
}
Exemplo n.º 3
0
static int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) {
    struct inode *inode = dentry->d_inode ;
    int error ;
    if (attr->ia_valid & ATTR_SIZE) {
	/* version 2 items will be caught by the s_maxbytes check
	** done for us in vmtruncate
	*/
	if (get_inode_item_key_version(inode) == KEY_FORMAT_3_5 &&
	    attr->ia_size > MAX_NON_LFS)
            return -EFBIG ;

	/* fill in hole pointers in the expanding truncate case. */
        if (attr->ia_size > inode->i_size) {
	    error = generic_cont_expand(inode, attr->ia_size) ;
	    if (inode->u.reiserfs_i.i_prealloc_count > 0) {
		struct reiserfs_transaction_handle th ;
		/* we're changing at most 2 bitmaps, inode + super */
		journal_begin(&th, inode->i_sb, 4) ;
		reiserfs_discard_prealloc (&th, inode);
		journal_end(&th, inode->i_sb, 4) ;
	    }
	    if (error)
	        return error ;
	}
    }

    if ((((attr->ia_valid & ATTR_UID) && (attr->ia_uid & ~0xffff)) ||
	 ((attr->ia_valid & ATTR_GID) && (attr->ia_gid & ~0xffff))) &&
	(get_inode_sd_version (inode) == STAT_DATA_V1))
		/* stat data of format v3.5 has 16 bit uid and gid */
	    return -EINVAL;

#ifdef CONFIG_REISERFS_IMMUTABLE_HACK 
    if (reiserfs_suid_immutable(inode->i_sb)) {
	uid_t uid;
	umode_t mode;

	uid = (attr->ia_valid & ATTR_UID) ? attr->ia_uid : inode->i_uid;
	mode = (attr->ia_valid & ATTR_MODE) ? attr->ia_mode : inode->i_mode;
	if (uid == 0 && (mode & S_ISUID) && !capable(CAP_LINUX_IMMUTABLE))
	    return -EPERM;
    }
#endif /* COCONFIG_REISERFS_IMMUTABLE_HACK */

    error = inode_change_ok(inode, attr) ;
#ifdef CONFIG_REISERFS_IMMUTABLE_HACK 
    if (!error && reiserfs_suid_immutable(inode->i_sb)) {
	uid_t uid;
	umode_t mode;

	uid = (attr->ia_valid & ATTR_UID) ? attr->ia_uid : inode->i_uid;
	mode = (attr->ia_valid & ATTR_MODE) ? attr->ia_mode : inode->i_mode;
	inode->i_flags &= ~S_IMMUTABLE;
	if (uid == 0 && (mode & S_ISUID)) {
	    inode->i_flags |= S_IMMUTABLE;
#ifdef CONFIG_REISERFS_IMMUTABLE_HACK_DEBUG 
	    printk("set IMMUTABLE, inode %p\n", inode); 
#endif /* CONFIG_REISERFS_IMMUTABLE_HACK_DEBUG */ 
	}
    }
#endif /* COCONFIG_REISERFS_IMMUTABLE_HACK */
    if (!error)
        inode_setattr(inode, attr) ;

    return error ;
}
Exemplo n.º 4
0
/* 
** We pre-allocate 8 blocks.  Pre-allocation is used for files > 16 KB only.
** This lowers fragmentation on large files by grabbing a contiguous set of
** blocks at once.  It also limits the number of times the bitmap block is
** logged by making X number of allocation changes in a single transaction.
**
** We are using a border to divide the disk into two parts.  The first part
** is used for tree blocks, which have a very high turnover rate (they
** are constantly allocated then freed)
**
** The second part of the disk is for the unformatted nodes of larger files.
** Putting them away from the tree blocks lowers fragmentation, and makes
** it easier to group files together.  There are a number of different
** allocation schemes being tried right now, each is documented below.
**
** A great deal of the allocator's speed comes because reiserfs_get_block
** sends us the block number of the last unformatted node in the file.  Once
** a given block is allocated past the border, we don't collide with the
** blocks near the search_start again.
** 
*/
int reiserfs_new_unf_blocknrs2 (struct reiserfs_transaction_handle *th, 
				struct inode       * p_s_inode,
				unsigned long      * free_blocknrs,
				unsigned long        search_start)
{
  int ret=0, blks_gotten=0;
  unsigned long border = 0;
  unsigned long bstart = 0;
  unsigned long hash_in, hash_out;
  unsigned long saved_search_start=search_start;
  int allocated[PREALLOCATION_SIZE];
  int blks;

  if (!reiserfs_no_border(th->t_super)) {
    /* we default to having the border at the 10% mark of the disk.  This
    ** is an arbitrary decision and it needs tuning.  It also needs a limit
    ** to prevent it from taking too much space on huge drives.
    */
    bstart = (SB_BLOCK_COUNT(th->t_super) / 10); 
  }
  if (!reiserfs_no_unhashed_relocation(th->t_super)) {
    /* this is a very simple first attempt at preventing too much grouping
    ** around the border value.  Since k_dir_id is never larger than the
    ** highest allocated oid, it is far from perfect, and files will tend
    ** to be grouped towards the start of the border
    */
    border = le32_to_cpu(INODE_PKEY(p_s_inode)->k_dir_id) % (SB_BLOCK_COUNT(th->t_super) - bstart - 1) ;
  } else {
    /* why would we want to delcare a local variable to this if statement
    ** name border????? -chris
    ** unsigned long border = 0;
    */
    if (!reiserfs_hashed_relocation(th->t_super)) {
      hash_in = le32_to_cpu((INODE_PKEY(p_s_inode))->k_dir_id);
				/* I wonder if the CPU cost of the
                                   hash will obscure the layout
                                   effect? Of course, whether that
                                   effect is good or bad we don't
                                   know.... :-) */
      
      hash_out = keyed_hash(((char *) (&hash_in)), 4);
      border = hash_out % (SB_BLOCK_COUNT(th->t_super) - bstart - 1) ;
    }
  }
  border += bstart ;
  allocated[0] = 0 ; /* important.  Allows a check later on to see if at
                      * least one block was allocated.  This prevents false
		      * no disk space returns
		      */

  if ( (p_s_inode->i_size < 4 * 4096) || 
       !(S_ISREG(p_s_inode->i_mode)) )
    {
      if ( search_start < border 
	   || (
				/* allow us to test whether it is a
                                   good idea to prevent files from
                                   getting too far away from their
                                   packing locality by some unexpected
                                   means.  This might be poor code for
                                   directories whose files total
                                   larger than 1/10th of the disk, and
                                   it might be good code for
                                   suffering from old insertions when the disk
                                   was almost full. */
               /* changed from !reiserfs_test3(th->t_super), which doesn't
               ** seem like a good idea.  Think about adding blocks to
               ** a large file.  If you've allocated 10% of the disk
               ** in contiguous blocks, you start over at the border value
               ** for every new allocation.  This throws away all the
               ** information sent in about the last block that was allocated
               ** in the file.  Not a good general case at all.
               ** -chris
               */
	       reiserfs_test4(th->t_super) && 
	       (search_start > border + (SB_BLOCK_COUNT(th->t_super) / 10))
	       )
	   )
	search_start=border;
  
      ret = do_reiserfs_new_blocknrs(th, free_blocknrs, search_start, 
				     1/*amount_needed*/, 
				     0/*use reserved blocks for root */,
				     1/*for_formatted*/,
				     0/*for prealloc */) ;  
      return ret;
    }

  /* take a block off the prealloc list and return it -Hans */
  if (p_s_inode->u.reiserfs_i.i_prealloc_count > 0) {
    p_s_inode->u.reiserfs_i.i_prealloc_count--;
    *free_blocknrs = p_s_inode->u.reiserfs_i.i_prealloc_block++;

    /* if no more preallocated blocks, remove inode from list */
    if (! p_s_inode->u.reiserfs_i.i_prealloc_count) {
      list_del(&p_s_inode->u.reiserfs_i.i_prealloc_list);
    }
    
    return ret;
  }

				/* else get a new preallocation for the file */
  reiserfs_discard_prealloc (th, p_s_inode);
  /* this uses the last preallocated block as the search_start.  discard
  ** prealloc does not zero out this number.
  */
  if (search_start <= p_s_inode->u.reiserfs_i.i_prealloc_block) {
    search_start = p_s_inode->u.reiserfs_i.i_prealloc_block;
  }
  
  /* doing the compare again forces search_start to be >= the border,
  ** even if the file already had prealloction done.  This seems extra,
  ** and should probably be removed
  */
  if ( search_start < border ) search_start=border; 

  /* If the disk free space is already below 10% we should 
  ** start looking for the free blocks from the beginning 
  ** of the partition, before the border line.
  */
  if ( SB_FREE_BLOCKS(th->t_super) <= (SB_BLOCK_COUNT(th->t_super) / 10) ) {
    search_start=saved_search_start;
  }

  *free_blocknrs = 0;
  blks = PREALLOCATION_SIZE-1;
  for (blks_gotten=0; blks_gotten<PREALLOCATION_SIZE; blks_gotten++) {
    ret = do_reiserfs_new_blocknrs(th, free_blocknrs, search_start, 
				   1/*amount_needed*/, 
				   0/*for root reserved*/,
				   1/*for_formatted*/,
				   (blks_gotten > 0)/*must_be_contiguous*/) ;
    /* if we didn't find a block this time, adjust blks to reflect
    ** the actual number of blocks allocated
    */ 
    if (ret != CARRY_ON) {
      blks = blks_gotten > 0 ? (blks_gotten - 1) : 0 ;
      break ;
    }
    allocated[blks_gotten]= *free_blocknrs;
#ifdef CONFIG_REISERFS_CHECK
    if ( (blks_gotten>0) && (allocated[blks_gotten] - allocated[blks_gotten-1]) != 1 ) {
      /* this should be caught by new_blocknrs now, checking code */
      reiserfs_warning("yura-1, reiserfs_new_unf_blocknrs2: pre-allocated not contiguous set of blocks!\n") ;
      reiserfs_free_block(th, allocated[blks_gotten]);
      blks = blks_gotten-1; 
      break;
    }
#endif
    if (blks_gotten==0) {
      p_s_inode->u.reiserfs_i.i_prealloc_block = *free_blocknrs;
    }
    search_start = *free_blocknrs; 
    *free_blocknrs = 0;
  }
  p_s_inode->u.reiserfs_i.i_prealloc_count = blks;
  *free_blocknrs = p_s_inode->u.reiserfs_i.i_prealloc_block;
  p_s_inode->u.reiserfs_i.i_prealloc_block++;

  /* if inode has preallocated blocks, link him to list */
  if (p_s_inode->u.reiserfs_i.i_prealloc_count) {
    list_add(&p_s_inode->u.reiserfs_i.i_prealloc_list,
	     &SB_JOURNAL(th->t_super)->j_prealloc_list);
  } 
  /* we did actually manage to get 1 block */
  if (ret != CARRY_ON && allocated[0] > 0) {
    return CARRY_ON ;
  }
  /* NO_MORE_UNUSED_CONTIGUOUS_BLOCKS should only mean something to
  ** the preallocation code.  The rest of the filesystem asks for a block
  ** and should either get it, or know the disk is full.  The code
  ** above should never allow ret == NO_MORE_UNUSED_CONTIGUOUS_BLOCK,
  ** as it doesn't send for_prealloc = 1 to do_reiserfs_new_blocknrs
  ** unless it has already successfully allocated at least one block.
  ** Just in case, we translate into a return value the rest of the
  ** filesystem can understand.
  **
  ** It is an error to change this without making the
  ** rest of the filesystem understand NO_MORE_UNUSED_CONTIGUOUS_BLOCKS
  ** If you consider it a bug to return NO_DISK_SPACE here, fix the rest
  ** of the fs first.
  */
  if (ret == NO_MORE_UNUSED_CONTIGUOUS_BLOCKS) {
#ifdef CONFIG_REISERFS_CHECK
    reiserfs_warning("reiser-2015: this shouldn't happen, may cause false out of disk space error");
#endif
     return NO_DISK_SPACE; 
  }
  return ret;
}