~ubuntu-branches/ubuntu/natty/hdparm/natty

1.1.11 by Michael Vogt
Import upstream version 8.9
1
/*
2
 * Based upon original code by Ric Wheeler and Mark Lord.
3
 *
4
 * Copyright (c) EMC Corporation 2008
5
 * Copyright (c) Mark Lord 2008
6
 *
7
 * You may use/distribute this freely, under the terms of either
8
 * (your choice) the GNU General Public License version 2,
9
 * or a BSD style license.
10
 */
1.1.13 by Stephen Gran
Import upstream version 9.27
11
#define _FILE_OFFSET_BITS 64
1.1.11 by Michael Vogt
Import upstream version 8.9
12
#include <unistd.h>
13
#include <string.h>
14
#include <stdlib.h>
15
#include <stdio.h>
16
#include <fcntl.h>
17
#include <errno.h>
18
#include <sys/ioctl.h>
19
#include <sys/stat.h>
20
#include <linux/types.h>
21
#include <linux/fs.h>
22
23
#include "hdparm.h"
24
1.1.13 by Stephen Gran
Import upstream version 9.27
25
static const unsigned int sector_bytes = 512; // FIXME someday
26
1.1.11 by Michael Vogt
Import upstream version 8.9
27
struct file_extent {
28
	__u64 byte_offset;
29
	__u64 first_block;
30
	__u64 last_block;
31
	__u64 block_count;
32
};
33
1.1.13 by Stephen Gran
Import upstream version 9.27
34
static void handle_extent (struct file_extent ext, unsigned int sectors_per_block, __u64 start_lba)
1.1.11 by Michael Vogt
Import upstream version 8.9
35
{
1.1.13 by Stephen Gran
Import upstream version 9.27
36
	char lba_info[64], len_info[32];
1.1.11 by Michael Vogt
Import upstream version 8.9
37
	__u64 begin_lba, end_lba;
38
	__u64 nsectors = ext.block_count * sectors_per_block;
39
40
	if (ext.first_block) {
1.1.12 by Stephen Gran
Import upstream version 9.15
41
		begin_lba = start_lba + ( ext.first_block     * sectors_per_block);
42
		end_lba   = start_lba + ((ext.last_block + 1) * sectors_per_block) - 1;
1.1.11 by Michael Vogt
Import upstream version 8.9
43
	} else {
44
		begin_lba = end_lba = 0;
45
	}
46
47
	if (ext.first_block)
48
		sprintf(lba_info, "%10llu %10llu", begin_lba, end_lba);
49
	else
50
		strcpy(lba_info, "      -          -   ");
1.1.13 by Stephen Gran
Import upstream version 9.27
51
	if (!ext.first_block && !nsectors)
52
		strcpy(len_info, "      -   ");
53
	else
54
		sprintf(len_info, "%10llu", nsectors);
55
	printf("%12llu %s %s\n", ext.byte_offset, lba_info, len_info);
1.1.11 by Michael Vogt
Import upstream version 8.9
56
}
57
1.1.13 by Stephen Gran
Import upstream version 9.27
58
static int walk_fibmap (int fd, struct stat *st, unsigned int sectors_per_block, __u64 start_lba)
1.1.11 by Michael Vogt
Import upstream version 8.9
59
{
1.1.13 by Stephen Gran
Import upstream version 9.27
60
	struct file_extent ext;
1.1.11 by Michael Vogt
Import upstream version 8.9
61
	unsigned long num_blocks;
1.1.13 by Stephen Gran
Import upstream version 9.27
62
	__u64 blk_idx, hole = ~0ULL;
1.1.11 by Michael Vogt
Import upstream version 8.9
63
64
	/*
65
	 * How many calls to FIBMAP do we need?
66
	 * FIBMAP returns a filesystem block number (counted from the start of the device)
67
	 * for each file block.  This can be converted to a disk LBA using the filesystem
68
	 * blocksize and LBA offset obtained earlier.
69
	 */
1.1.13 by Stephen Gran
Import upstream version 9.27
70
	num_blocks = (st->st_size + st->st_blksize - 1) / st->st_blksize;
1.1.11 by Michael Vogt
Import upstream version 8.9
71
	memset(&ext, 0, sizeof(ext));
72
73
	/*
74
	 * Loop through the file, building a map of the extents.
75
	 * All of this is done in filesystem blocks size (fs_blksize) units.
76
	 *
77
	 * Assumptions:
78
	 * Throughout the file, there can be any number of blocks backed by holes
79
	 * or by allocated blocks.  Tail-packed files are special - if we find a file
1.1.13 by Stephen Gran
Import upstream version 9.27
80
	 * that has a size and has no allocated blocks, we could flag it as a "tail-packed"
81
	 * file if we cared: data is packed into the tail space of the inode block.
1.1.11 by Michael Vogt
Import upstream version 8.9
82
	 */
83
	for (blk_idx = 0; blk_idx < num_blocks; blk_idx++) {
1.1.13 by Stephen Gran
Import upstream version 9.27
84
		unsigned int blknum = blk_idx;
85
		__u64 blknum64;
1.1.11 by Michael Vogt
Import upstream version 8.9
86
		/*
87
		 * FIBMAP takes a block index as input and on return replaces it with a
88
		 * block number relative to the beginning of the filesystem/partition.
89
		 * An output value of zero means "unallocated", or a "hole" in a sparse file.
1.1.13 by Stephen Gran
Import upstream version 9.27
90
		 * Note that this is a 32-bit value, so it will not work properly on
91
		 * files/filesystems with more than 4 billion blocks (~16TB),
1.1.11 by Michael Vogt
Import upstream version 8.9
92
		 */
93
		if (ioctl(fd, FIBMAP, &blknum) == -1) {
1.1.13 by Stephen Gran
Import upstream version 9.27
94
			int err = errno;
1.1.11 by Michael Vogt
Import upstream version 8.9
95
			perror("ioctl(FIBMAP)");
96
			return err;
97
		}
1.1.13 by Stephen Gran
Import upstream version 9.27
98
		blknum64 = blknum;	/* work in 64-bits as much as possible */
1.1.11 by Michael Vogt
Import upstream version 8.9
99
1.1.13 by Stephen Gran
Import upstream version 9.27
100
		if (blk_idx && blknum64 == (ext.last_block + 1)) {
1.1.11 by Michael Vogt
Import upstream version 8.9
101
			/*
102
			 * Continuation of extent: Bump last_block and block_count.
103
			 */
1.1.13 by Stephen Gran
Import upstream version 9.27
104
			ext.last_block = blknum64 ? blknum64 : hole;
1.1.11 by Michael Vogt
Import upstream version 8.9
105
			ext.block_count++;
106
		} else {
107
			/*
108
			 * New extent: print previous extent (if any), and re-init the extent record.
109
			 */
1.1.13 by Stephen Gran
Import upstream version 9.27
110
			if (blk_idx)
111
				handle_extent(ext, sectors_per_block, start_lba);
112
			ext.first_block = blknum64;
113
			ext.last_block  = blknum64 ? blknum64 : hole;
1.1.11 by Michael Vogt
Import upstream version 8.9
114
			ext.block_count = 1;
1.1.13 by Stephen Gran
Import upstream version 9.27
115
			ext.byte_offset = blk_idx * st->st_blksize;
116
		}
117
	}
118
	handle_extent(ext, sectors_per_block, start_lba);
119
	return 0;
120
}
121
122
#define FE_COUNT	8000
123
#define FE_FLAG_LAST	(1 <<  0)
124
#define FE_FLAG_UNKNOWN	(1 <<  1)
125
#define FE_FLAG_UNALLOC	(1 <<  2)
126
#define FE_FLAG_NOALIGN	(1 <<  8)
127
128
#define EXTENT_UNKNOWN (FE_FLAG_UNKNOWN | FE_FLAG_UNALLOC | FE_FLAG_NOALIGN)
129
130
struct fe_s {
131
	__u64 logical;
132
	__u64 physical;
133
	__u64 length;
134
	__u64 reserved64[2];
135
	__u32 flags;
136
	__u32 reserved32[3];
137
};
138
139
struct fm_s {
140
	__u64 start;
141
	__u64 length;
142
	__u32 flags;
143
	__u32 mapped_extents;
144
	__u32 extent_count;
145
	__u32 reserved;
146
};
147
148
struct fs_s {
149
	struct fm_s fm;
150
	struct fe_s fe[FE_COUNT];
151
};
152
153
#define FIEMAP	_IOWR('f', 11, struct fm_s)
154
155
static int walk_fiemap (int fd, unsigned int sectors_per_block, __u64 start_lba)
156
{
157
	unsigned int i, done = 0;
158
	unsigned int blksize = sectors_per_block * sector_bytes;
159
	struct fs_s fs;
160
161
	memset(&fs, 0, sizeof(fs));
162
	do {
163
		fs.fm.length = ~0ULL;
164
		fs.fm.flags  = 0;
165
		fs.fm.extent_count = FE_COUNT;
166
167
		if (-1 == ioctl(fd, FIEMAP, &fs)) {
168
			int err = errno;
169
			//perror("ioctl(FIEMAP)");
170
			return err;
171
		}
172
173
		if (0) fprintf(stderr, "ioctl(FIEMAP) returned %llu extents\n", (__u64)fs.fm.mapped_extents);
174
		if (!fs.fm.mapped_extents) {
175
			done = 1;
176
		} else {
177
			struct file_extent ext;
178
			memset(&ext, 0, sizeof(ext));
179
			for (i = 0; i < fs.fm.mapped_extents; i++) {
180
				__u64 phy_blk, ext_len;
181
182
				ext.byte_offset = fs.fe[i].logical;
183
				if (0) fprintf(stderr, "log=%llu phy=%llu len=%llu flags=0x%x\n", fs.fe[i].logical,
184
						fs.fe[i].physical, fs.fe[i].length, fs.fe[i].flags);
185
				if (fs.fe[i].flags & EXTENT_UNKNOWN) {
186
					ext.first_block = 0;
187
					ext.last_block  = 0;
188
					ext.block_count = 0; /* FIEMAP returns garbage for this. Ugh. */
189
				} else {
190
					phy_blk = fs.fe[i].physical / blksize;
191
					ext_len = fs.fe[i].length   / blksize;
192
193
					ext.first_block = phy_blk;
194
					ext.last_block  = phy_blk + ext_len - 1;
195
					ext.block_count = ext_len;
196
				}
197
				handle_extent(ext, sectors_per_block, start_lba);
198
199
				if (fs.fe[i].flags & FE_FLAG_LAST) {
200
					/*
201
					 * Hit an ext4 bug in 2.6.29.4, where some FIEMAP calls
202
					 * had the LAST flag set in the final returned extent,
203
					 * even though there were *plenty* more extents to be had
204
					 * from continued FIEMAP calls.
205
					 *
206
					 * So, we'll ignore it here, and instead rely on getting
207
					 * a zero count back from fs.fm.mapped_extents at the end.
208
					 */
209
					if (0) fprintf(stderr, "%s: ignoring LAST bit\n", __func__);
210
					//done = 1;
211
				}
212
213
			}
214
			fs.fm.start = (fs.fe[i-1].logical + fs.fe[i-1].length);
215
		}
216
	} while (!done);
217
	return 0;
218
}
219
220
int do_filemap (const char *file_name)
221
{
222
	int fd, err;
223
	struct stat st;
224
	__u64 start_lba = 0;
225
	unsigned int sectors_per_block;
226
227
	if ((fd = open(file_name, O_RDONLY)) == -1) {
228
		err = errno;
229
		perror(file_name);
230
		return err;
231
	}
232
	if (fstat(fd, &st) == -1) {
233
		err = errno;
234
		perror(file_name);
235
		return err;
236
	}
237
	if (!S_ISREG(st.st_mode)) {
238
		fprintf(stderr, "%s: not a regular file\n", file_name);
239
		close(fd);
240
		return EINVAL;
241
	}
242
243
	/*
244
	 * Get the filesystem starting LBA:
245
	 */
246
	err = get_dev_t_geometry(st.st_dev, NULL, NULL, NULL, &start_lba, NULL);
247
	if (err) {
248
		close(fd);
249
		return err;
250
	}
251
252
	sectors_per_block = st.st_blksize / sector_bytes;
253
	printf("\n%s:\n filesystem blocksize %lu, begins at LBA %llu;"
254
	       " assuming %u byte sectors.\n",
255
	       file_name, (unsigned long)st.st_blksize, start_lba, sector_bytes);
256
	printf("%12s %10s %10s %10s\n", "byte_offset", "begin_LBA", "end_LBA", "sectors");
257
258
	if (st.st_size == 0) {
259
		struct file_extent ext;
260
		memset(&ext, 0, sizeof(ext));
261
		handle_extent(ext, sectors_per_block, start_lba);
262
		close(fd);
263
		return 0;
264
	}
265
266
	err = walk_fiemap(fd, sectors_per_block, start_lba);
267
	if (err)
268
		err = walk_fibmap(fd, &st, sectors_per_block, start_lba);
1.1.11 by Michael Vogt
Import upstream version 8.9
269
	close (fd);
270
	return 0;
271
}