summaryrefslogtreecommitdiffstats
path: root/contrib/android/fsmap.c
blob: 9ee8472dc4f61ba3101e7052b83472ac5ed7194d (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include "fsmap.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "support/nls-enable.h"

struct walk_ext_priv_data {
	char			*path;
	ext2_filsys		fs;
	struct fsmap_format	*format;
};

static int walk_block(ext2_filsys fs  EXT2FS_ATTR((unused)), blk64_t *blocknr,
		      e2_blkcnt_t blockcnt,
		      blk64_t ref64_blk EXT2FS_ATTR((unused)),
		      int ref_offset EXT2FS_ATTR((unused)),
		      void *priv)
{
	struct walk_ext_priv_data *pdata = priv;
	struct fsmap_format *format = pdata->format;

	return format->add_block(fs, *blocknr, blockcnt < 0, format->private);
}

static errcode_t ino_iter_extents(ext2_filsys fs, ext2_ino_t ino,
				  ext2_extent_handle_t extents,
				  struct walk_ext_priv_data *pdata)
{
	blk64_t block;
	errcode_t retval;
	blk64_t next_lblk = 0;
	int op = EXT2_EXTENT_ROOT;
	struct ext2fs_extent extent;
	struct fsmap_format *format = pdata->format;

	for (;;) {
		retval = ext2fs_extent_get(extents, op, &extent);
		if (retval)
			break;

		op = EXT2_EXTENT_NEXT;

		if ((extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) ||
		    !(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF))
			continue;

		for (; next_lblk < extent.e_lblk; next_lblk++)
			format->add_block(fs, 0, 0, format->private);

		block = extent.e_pblk;
		for (; next_lblk < extent.e_lblk + extent.e_len; next_lblk++)
			format->add_block(fs, block++, 0, format->private);
	}

	if (retval == EXT2_ET_EXTENT_NO_NEXT)
		retval = 0;
	if (retval) {
		com_err(__func__, retval, ("getting extents of ino \"%u\""),
			ino);
	}
	return retval;
}

static errcode_t ino_iter_blocks(ext2_filsys fs, ext2_ino_t ino,
				 struct walk_ext_priv_data *pdata)
{
	errcode_t retval;
	struct ext2_inode inode;
	ext2_extent_handle_t extents;
	struct fsmap_format *format = pdata->format;

	retval = ext2fs_read_inode(fs, ino, &inode);
	if (retval)
		return retval;

	if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
		return format->inline_data(&(inode.i_block[0]),
					   format->private);

	retval = ext2fs_extent_open(fs, ino, &extents);
	if (retval == EXT2_ET_INODE_NOT_EXTENT) {
		retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY,
			NULL, walk_block, pdata);
		if (retval) {
			com_err(__func__, retval, _("listing blocks of ino \"%u\""),
				ino);
		}
		return retval;
	}

	retval = ino_iter_extents(fs, ino, extents, pdata);

	ext2fs_extent_free(extents);
	return retval;
}

static int is_dir(ext2_filsys fs, ext2_ino_t ino)
{
	struct ext2_inode inode;

	if (ext2fs_read_inode(fs, ino, &inode))
		return 0;
	return S_ISDIR(inode.i_mode);
}

static int walk_ext_dir(ext2_ino_t dir EXT2FS_ATTR((unused)),
			int flags EXT2FS_ATTR((unused)),
			struct ext2_dir_entry *de,
			int offset EXT2FS_ATTR((unused)),
			int blocksize EXT2FS_ATTR((unused)),
			char *buf EXT2FS_ATTR((unused)), void *priv_data)
{
	errcode_t retval;
	struct ext2_inode inode;
	char *filename, *cur_path, *name = de->name;
	int name_len = de->name_len & 0xff;
	struct walk_ext_priv_data *pdata = priv_data;
	struct fsmap_format *format = pdata->format;

	if (!strncmp(name, ".", name_len)
	    || !strncmp(name, "..", name_len)
	    || !strncmp(name, "lost+found", 10))
		return 0;

	if (asprintf(&filename, "%s/%.*s", pdata->path, name_len, name) < 0)
		return -ENOMEM;

	retval = ext2fs_read_inode(pdata->fs, de->inode, &inode);
	if (retval) {
		com_err(__func__, retval, _("reading ino \"%u\""), de->inode);
		goto end;
	}
	format->start_new_file(filename, de->inode, &inode, format->private);
	retval = ino_iter_blocks(pdata->fs, de->inode, pdata);
	if (retval)
		return retval;
	format->end_new_file(format->private);

	retval = 0;
	if (is_dir(pdata->fs, de->inode)) {
		cur_path = pdata->path;
		pdata->path = filename;
		retval = ext2fs_dir_iterate2(pdata->fs, de->inode, 0, NULL,
				    walk_ext_dir, pdata);
		pdata->path = cur_path;
	}

end:
	free(filename);
	return retval;
}

errcode_t fsmap_iter_filsys(ext2_filsys fs, struct fsmap_format *format,
			    const char *file, const char *mountpoint)
{
	struct walk_ext_priv_data pdata;
	errcode_t retval;

	format->private = format->init(file, mountpoint);
	pdata.fs = fs;
	pdata.path = "";
	pdata.format = format;

	retval = ext2fs_dir_iterate2(fs, EXT2_ROOT_INO, 0, NULL, walk_ext_dir, &pdata);

	format->cleanup(format->private);
	return retval;
}