Ejemplo n.º 1
0
/*
 * Delete and free clusters if needed.  This only works with DEPTH_TRAVERSE.
 */
static int truncate_iterate(ocfs2_filesys *fs,
			    struct ocfs2_extent_rec *rec,
			    int tree_depth, uint32_t ccount,
			    uint64_t ref_blkno, int ref_recno,
			    void *priv_data)
{
	struct truncate_ctxt *ctxt = (struct truncate_ctxt *)priv_data;
	uint32_t len = 0, new_size_in_clusters = ctxt->new_size_in_clusters;
	uint64_t start = 0;
	errcode_t ret;
	int func_ret = OCFS2_EXTENT_ERROR;
	char *buf = NULL;
	struct ocfs2_extent_list *el = NULL;
	int cleanup_rec = 0;

	if ((rec->e_cpos + ocfs2_rec_clusters(tree_depth, rec)) <=
							new_size_in_clusters)
		return 0;

	if (rec->e_cpos >= new_size_in_clusters) {
		/* the rec is entirely outside the new size, free it */
		if (!tree_depth) {
			start = rec->e_blkno;
			len = ocfs2_rec_clusters(tree_depth, rec);
		} else {
			/* here we meet with a full empty extent block, delete
			 * it. The extent list it contains should already be
			 * iterated and all the clusters have been freed.
			 */
			ret = ocfs2_delete_extent_block(fs, rec->e_blkno);
			if (ret)
				goto bail;
		}

		cleanup_rec = 1;
	} else {
		/* we're truncating into the middle of the rec */
		len = rec->e_cpos +
			 ocfs2_rec_clusters(tree_depth, rec);
		len -= new_size_in_clusters;
		if (!tree_depth) {
			ocfs2_set_rec_clusters(tree_depth, rec,
				 	new_size_in_clusters - rec->e_cpos);
			start = rec->e_blkno +
				ocfs2_clusters_to_blocks(fs,
						ocfs2_rec_clusters(tree_depth,
								   rec));
		} else {
			ocfs2_set_rec_clusters(tree_depth, rec,
					new_size_in_clusters - rec->e_cpos);
			/*
			 * For a sparse file, we may meet with another
			 * situation here:
			 * The start of the left most extent rec is greater
			 * than the new size we truncate the file to, but the
			 * start of the extent block is less than that size.
			 * In this case, actually all the extent records in
			 * this extent block have been removed. So we have
			 * to remove the extent block also.
			 * In this function, we have to reread the extent list
			 * to see whether the extent block is empty or not.
			 */
			ret = ocfs2_malloc_block(fs->fs_io, &buf);
			if (ret)
				goto bail;

			ret = ocfs2_read_extent_block(fs, rec->e_blkno, buf);
			if (ret)
				goto bail;

			el = &((struct ocfs2_extent_block *)buf)->h_list;
			if (el->l_next_free_rec == 0) {
				ret = ocfs2_delete_extent_block(fs, rec->e_blkno);
				if (ret)
					goto bail;
				cleanup_rec = 1;
			}
		}
	}

	if (start) {
		if (ctxt->free_clusters)
			ret = ctxt->free_clusters(fs, len, start,
						  ctxt->free_data);
		else
			ret = ocfs2_truncate_clusters(fs, rec, ctxt->ino,
						      len, start);
		if (ret)
			goto bail;
		ctxt->new_i_clusters -= len;
	}

	func_ret =  OCFS2_EXTENT_CHANGED;
bail:
	if (cleanup_rec)
		memset(rec, 0, sizeof(struct ocfs2_extent_rec));
	if (buf)
		ocfs2_free(&buf);
	return func_ret;
}
Ejemplo n.º 2
0
/* the caller will check if er->e_blkno is out of range to determine if it
 * should try removing the record */
static errcode_t check_er(o2fsck_state *ost, struct extent_info *ei,
			  struct ocfs2_dinode *di,
			  struct ocfs2_extent_list *el,
			  struct ocfs2_extent_rec *er, int *changed)
{
	errcode_t ret = 0;
	uint64_t first_block;
	uint32_t last_cluster, clusters;

	clusters = ocfs2_rec_clusters(el->l_tree_depth, er);
	verbosef("cpos %u clusters %u blkno %"PRIu64"\n", er->e_cpos,
		 clusters, (uint64_t)er->e_blkno);

	if (ocfs2_block_out_of_range(ost->ost_fs, er->e_blkno))
		goto out;

	if (el->l_tree_depth) {
		int is_valid = 0;
		/* we only expect a given depth when we descend to extent blocks
		 * from a previous depth.  these start at 0 when the inode
		 * is checked */
		ei->ei_expect_depth = 1;
		ei->ei_expected_depth = el->l_tree_depth - 1;
		check_eb(ost, ei, di, er->e_blkno, &is_valid);
		if (!is_valid && 
		    prompt(ost, PY, PR_EXTENT_EB_INVALID,
			   "The extent record for cluster offset "
			   "%"PRIu32" in inode %"PRIu64" refers to an invalid "
			   "extent block at %"PRIu64".  Clear the reference "
			   "to this invalid block?", er->e_cpos,
			   (uint64_t)di->i_blkno, (uint64_t)er->e_blkno)) {

			er->e_blkno = 0;
			*changed = 1;
		}
		ret = 0;
		goto out;
	}

	if (!ocfs2_writes_unwritten_extents(OCFS2_RAW_SB(ost->ost_fs->fs_super)) &&
	    (er->e_flags & OCFS2_EXT_UNWRITTEN) &&
	    prompt(ost, PY, PR_EXTENT_MARKED_UNWRITTEN,
		   "The extent record for cluster offset %"PRIu32" "
		   "in inode %"PRIu64" has the UNWRITTEN flag set, but "
		   "this filesystem does not support unwritten extents.  "
		   "Clear the UNWRITTEN flag?", er->e_cpos,
		   (uint64_t)di->i_blkno)) {
		er->e_flags &= ~OCFS2_EXT_UNWRITTEN;
	}

	first_block = ocfs2_blocks_to_clusters(ost->ost_fs, er->e_blkno);
	first_block = ocfs2_clusters_to_blocks(ost->ost_fs, first_block);

	if (first_block != er->e_blkno &&
	    prompt(ost, PY, PR_EXTENT_BLKNO_UNALIGNED,
		   "The extent record for cluster offset %"PRIu32" "
		   "in inode %"PRIu64" refers to block %"PRIu64" which isn't "
		   "aligned with the start of a cluster.  Point the extent "
		   "record at block %"PRIu64" which starts this cluster?",
		   er->e_cpos, (uint64_t)di->i_blkno,
		   (uint64_t)er->e_blkno, first_block)) {

		er->e_blkno = first_block;
		*changed = 1;
	}

	/* imagine blkno 0, 1 er_clusters.  last_cluster is 1 and 
	 * fs_clusters is 1, which is ok.. */
	last_cluster = ocfs2_blocks_to_clusters(ost->ost_fs, er->e_blkno) +
		       clusters;

	if (last_cluster > ost->ost_fs->fs_clusters &&
	    prompt(ost, PY, PR_EXTENT_CLUSTERS_OVERRUN,
		   "The extent record for cluster offset %"PRIu32" "
		   "in inode %"PRIu64" refers to an extent that goes beyond "
		   "the end of the volume.  Truncate the extent by %"PRIu32" "
		   "clusters to fit it in the volume?", er->e_cpos,
		   (uint64_t)di->i_blkno,
		   last_cluster - ost->ost_fs->fs_clusters)) {

		clusters -= last_cluster - ost->ost_fs->fs_clusters;
		ocfs2_set_rec_clusters(el->l_tree_depth, er, clusters);
		*changed = 1;
	}
	
	/* XXX offer to remove leaf records with er_clusters set to 0? */

	/* XXX check that the blocks that are referenced aren't already 
	 * used */

out:
	return ret;
}