summaryrefslogtreecommitdiff
path: root/resize/banalysis.c
blob: 233158b613872c3ad99c2a5cd0512f8a38eefc28 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*
 * banalysis.c --- Analyze a filesystem for a block struct
 *
 * Copyright (C) 1997 Theodore Ts'o.  This file may be redistributed
 * under the terms of the GNU Public License.
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>

#ifdef HAVE_LINUX_FS_H
#include <linux/fs.h>
#endif
#include <linux/ext2_fs.h>

#include "ext2fs/ext2fs.h"

#include "ext2fs/brel.h"
#include "banalysis.h"

struct process_block_struct {
	struct ext2_block_analyzer_funcs *funcs;
	struct ext2_inode_context *ctx;
	void *private;
};

/*
 * This function returns 1 if the inode's block entries actually
 * contain block entries.
 */
static int inode_has_valid_blocks(struct ext2_inode *inode)
{
	/*
	 * Only directories, regular files, and some symbolic links
	 * have valid block entries.
	 */
	if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) &&
	    !LINUX_S_ISLNK(inode->i_mode))
		return 0;
	
	/*
	 * If the symbolic link is a "fast symlink", then the symlink
	 * target is stored in the block entries.
	 */
	if (LINUX_S_ISLNK (inode->i_mode) && inode->i_blocks == 0 &&
	    inode->i_size < EXT2_N_BLOCKS * sizeof (unsigned long))
		return 0;

	return 1;
}

static int process_block(ext2_filsys fs, blk_t	*block_nr,
			 int blockcnt, blk_t ref_block,
			 int ref_offset, void *private)
{
	struct process_block_struct *pb = private;
	blk_t	new_block;
	struct ext2_block_relocate_entry ent;

	if (ref_block == 0)
		ref_offset = blockcnt;

	new_block = pb->funcs->block_analyze(fs, *block_nr, ref_block,
					     ref_offset, pb->ctx, pb->private);
	if (new_block) {
		ent.new = new_block;
		ent.offset = ref_offset;
		if (ref_block) {
			ent.owner.block_ref = ref_block;
			ent.flags = 0;
		} else {
			ent.owner.inode_ref = pb->ctx->ino;
			ent.flags = RELOCATE_INODE_REF;
		}
		ext2fs_brel_put(pb->ctx->brel, *block_nr, &ent);
	}
	return 0;
}

errcode_t ext2_block_analyze(ext2_filsys fs,
			     struct ext2_block_analyzer_funcs *funcs,
			     ext2_brel block_relocation_table,
			     void *private)
{
	ino_t	ino;
	struct ext2_inode inode;
	errcode_t	retval;
	struct process_block_struct pb;
	struct ext2_inode_context ctx;
	ext2_inode_scan	scan;
	char		*block_buf;
	
	retval = ext2fs_open_inode_scan(fs, 0, &scan);
	if (retval)
		return retval;

	pb.funcs = funcs;
	pb.private = private;
	pb.ctx = &ctx;
	
	block_buf = malloc(fs->blocksize * 3);
	if (!block_buf)
		return ENOMEM;

	retval = ext2fs_get_next_inode(scan, &ino, &inode);
	if (retval)
		return retval;
	ctx.ctx = private;
	ctx.brel = block_relocation_table;
	while (ino) {
		if ((inode.i_links_count == 0) ||
		    !inode_has_valid_blocks(&inode))
			goto next;
		
		ctx.ino = ino;
		ctx.inode = &inode;
		ctx.error = 0;

		if (funcs->pre_analyze &&
		    !(*funcs->pre_analyze)(fs, &ctx, private))
			goto next;

		retval = ext2fs_block_iterate2(fs, ino, 0, block_buf,
					      process_block, &pb);
		if (retval)
			return retval;

		if (funcs->post_analyze) 
			(funcs->post_analyze)(fs, &ctx, private);

	next:
		retval = ext2fs_get_next_inode(scan, &ino, &inode);
		if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
			goto next;
	}
	return 0;
}