]>
Commit | Line | Data |
---|---|---|
96424130 TT |
1 | /* |
2 | * filefrag.c -- report if a particular file is fragmented | |
efc6f628 | 3 | * |
96424130 TT |
4 | * Copyright 2003 by Theodore Ts'o. |
5 | * | |
6 | * %Begin-Header% | |
7 | * This file may be redistributed under the terms of the GNU Public | |
8 | * License. | |
9 | * %End-Header% | |
10 | */ | |
11 | ||
d1154eb4 | 12 | #include "config.h" |
d605ba11 | 13 | #ifndef __linux__ |
b34cbddb MA |
14 | #include <stdio.h> |
15 | #include <stdlib.h> | |
e62847c5 | 16 | #include <unistd.h> |
b34cbddb MA |
17 | |
18 | int main(void) { | |
2508eaa7 AD |
19 | fputs("This program is only supported on Linux!\n", stderr); |
20 | exit(EXIT_FAILURE); | |
b34cbddb MA |
21 | } |
22 | #else | |
4e222d9b TT |
23 | #ifndef _LARGEFILE_SOURCE |
24 | #define _LARGEFILE_SOURCE | |
25 | #endif | |
26 | #ifndef _LARGEFILE64_SOURCE | |
96424130 | 27 | #define _LARGEFILE64_SOURCE |
4e222d9b TT |
28 | #endif |
29 | ||
96424130 TT |
30 | |
31 | #include <stdio.h> | |
32 | #include <stdlib.h> | |
33 | #include <unistd.h> | |
34 | #include <string.h> | |
35 | #include <time.h> | |
36 | #include <fcntl.h> | |
37 | #include <errno.h> | |
6b394c1c TT |
38 | #ifdef HAVE_GETOPT_H |
39 | #include <getopt.h> | |
40 | #else | |
41 | extern char *optarg; | |
42 | extern int optind; | |
43 | #endif | |
96424130 TT |
44 | #include <sys/types.h> |
45 | #include <sys/stat.h> | |
46 | #include <sys/vfs.h> | |
aa3a2fe4 | 47 | #include <sys/ioctl.h> |
96424130 | 48 | #include <linux/fd.h> |
2508eaa7 | 49 | #include <ext2fs/ext2fs.h> |
e62847c5 KS |
50 | #include <ext2fs/ext2_types.h> |
51 | #include <ext2fs/fiemap.h> | |
96424130 TT |
52 | |
53 | int verbose = 0; | |
2508eaa7 | 54 | int blocksize; /* Use specified blocksize (default 1kB) */ |
e62847c5 KS |
55 | int sync_file = 0; /* fsync file before getting the mapping */ |
56 | int xattr_map = 0; /* get xattr mapping */ | |
2508eaa7 AD |
57 | int force_bmap; /* force use of FIBMAP instead of FIEMAP */ |
58 | int force_extent; /* print output in extent format always */ | |
59 | int logical_width = 8; | |
60 | int physical_width = 10; | |
577c773a TT |
61 | const char *ext_fmt = "%4d: %*llu..%*llu: %*llu..%*llu: %6llu: %s\n"; |
62 | const char *hex_fmt = "%4d: %*llx..%*llx: %*llx..%*llx: %6llx: %s\n"; | |
e62847c5 KS |
63 | |
64 | #define FILEFRAG_FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR) | |
96424130 | 65 | |
e62847c5 KS |
66 | #define FIBMAP _IO(0x00, 1) /* bmap access */ |
67 | #define FIGETBSZ _IO(0x00, 2) /* get the block size used for bmap */ | |
96424130 | 68 | |
2508eaa7 AD |
69 | #define LUSTRE_SUPER_MAGIC 0x0BD00BD0 |
70 | ||
e62847c5 | 71 | #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ |
3d16b3f4 TT |
72 | #define EXT3_IOC_GETFLAGS _IOR('f', 1, long) |
73 | ||
e62847c5 KS |
74 | static int int_log2(int arg) |
75 | { | |
76 | int l = 0; | |
77 | ||
78 | arg >>= 1; | |
79 | while (arg) { | |
80 | l++; | |
81 | arg >>= 1; | |
82 | } | |
83 | return l; | |
84 | } | |
85 | ||
86 | static int int_log10(unsigned long long arg) | |
87 | { | |
88 | int l = 0; | |
89 | ||
90 | arg = arg / 10; | |
91 | while (arg) { | |
92 | l++; | |
93 | arg = arg / 10; | |
94 | } | |
95 | return l; | |
96 | } | |
97 | ||
69022e02 TT |
98 | static unsigned int div_ceil(unsigned int a, unsigned int b) |
99 | { | |
100 | if (!a) | |
101 | return 0; | |
102 | return ((a - 1) / b) + 1; | |
103 | } | |
104 | ||
e62847c5 | 105 | static int get_bmap(int fd, unsigned long block, unsigned long *phy_blk) |
96424130 TT |
106 | { |
107 | int ret; | |
8198e791 | 108 | unsigned int b; |
96424130 TT |
109 | |
110 | b = block; | |
e62847c5 | 111 | ret = ioctl(fd, FIBMAP, &b); /* FIBMAP takes pointer to integer */ |
29758d2f AD |
112 | if (ret < 0) |
113 | return -errno; | |
e62847c5 KS |
114 | *phy_blk = b; |
115 | ||
116 | return ret; | |
117 | } | |
118 | ||
2508eaa7 AD |
119 | static void print_extent_header(void) |
120 | { | |
121 | printf(" ext: %*s %*s length: %*s flags:\n", | |
122 | logical_width * 2 + 3, | |
123 | "logical_offset:", | |
124 | physical_width * 2 + 3, "physical_offset:", | |
125 | physical_width + 1, | |
126 | "expected:"); | |
127 | } | |
128 | ||
29758d2f AD |
129 | static void print_flag(__u32 *flags, __u32 mask, char *buf, const char *name) |
130 | { | |
131 | if ((*flags & mask) == 0) | |
132 | return; | |
133 | ||
134 | strcat(buf, name); | |
135 | *flags &= ~mask; | |
136 | } | |
137 | ||
e62847c5 | 138 | static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex, |
2508eaa7 AD |
139 | unsigned long long expected, int blk_shift, |
140 | ext2fs_struct_stat *st) | |
e62847c5 | 141 | { |
2508eaa7 | 142 | unsigned long long physical_blk; |
e62847c5 | 143 | unsigned long long logical_blk; |
2508eaa7 AD |
144 | unsigned long long ext_len; |
145 | unsigned long long ext_blks; | |
29758d2f | 146 | __u32 fe_flags, mask; |
e62847c5 KS |
147 | char flags[256] = ""; |
148 | ||
2508eaa7 | 149 | /* For inline data all offsets should be in bytes, not blocks */ |
e62847c5 KS |
150 | if (fm_extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE) |
151 | blk_shift = 0; | |
152 | ||
153 | ext_len = fm_extent->fe_length >> blk_shift; | |
2508eaa7 | 154 | ext_blks = (fm_extent->fe_length - 1) >> blk_shift; |
e62847c5 | 155 | logical_blk = fm_extent->fe_logical >> blk_shift; |
29758d2f AD |
156 | if (fm_extent->fe_flags & FIEMAP_EXTENT_UNKNOWN) { |
157 | physical_blk = 0; | |
158 | } else { | |
159 | physical_blk = fm_extent->fe_physical >> blk_shift; | |
160 | } | |
2508eaa7 AD |
161 | |
162 | if (expected) | |
163 | sprintf(flags, ext_fmt == hex_fmt ? "%*llx: " : "%*llu: ", | |
164 | physical_width, expected >> blk_shift); | |
165 | else | |
166 | sprintf(flags, "%.*s ", physical_width, " "); | |
e62847c5 | 167 | |
29758d2f AD |
168 | fe_flags = fm_extent->fe_flags; |
169 | print_flag(&fe_flags, FIEMAP_EXTENT_LAST, flags, "last,"); | |
170 | print_flag(&fe_flags, FIEMAP_EXTENT_UNKNOWN, flags, "unknown_loc,"); | |
171 | print_flag(&fe_flags, FIEMAP_EXTENT_DELALLOC, flags, "delalloc,"); | |
172 | print_flag(&fe_flags, FIEMAP_EXTENT_ENCODED, flags, "encoded,"); | |
173 | print_flag(&fe_flags, FIEMAP_EXTENT_DATA_ENCRYPTED, flags,"encrypted,"); | |
174 | print_flag(&fe_flags, FIEMAP_EXTENT_NOT_ALIGNED, flags, "not_aligned,"); | |
175 | print_flag(&fe_flags, FIEMAP_EXTENT_DATA_INLINE, flags, "inline,"); | |
176 | print_flag(&fe_flags, FIEMAP_EXTENT_DATA_TAIL, flags, "tail_packed,"); | |
177 | print_flag(&fe_flags, FIEMAP_EXTENT_UNWRITTEN, flags, "unwritten,"); | |
178 | print_flag(&fe_flags, FIEMAP_EXTENT_MERGED, flags, "merged,"); | |
179 | print_flag(&fe_flags, FIEMAP_EXTENT_SHARED, flags, "shared,"); | |
180 | /* print any unknown flags as hex values */ | |
181 | for (mask = 1; fe_flags != 0 && mask != 0; mask <<= 1) { | |
17a1f2c1 | 182 | char hex[sizeof(mask) * 2 + 4]; /* 2 chars/byte + 0x, + NUL */ |
29758d2f | 183 | |
f2ca701c | 184 | if ((fe_flags & mask) == 0) |
29758d2f AD |
185 | continue; |
186 | sprintf(hex, "%#04x,", mask); | |
187 | print_flag(&fe_flags, mask, flags, hex); | |
188 | } | |
189 | ||
4e222d9b TT |
190 | if (fm_extent->fe_logical + fm_extent->fe_length >= |
191 | (unsigned long long) st->st_size) | |
e62847c5 KS |
192 | strcat(flags, "eof,"); |
193 | ||
194 | /* Remove trailing comma, if any */ | |
29758d2f AD |
195 | if (flags[0] != '\0') |
196 | flags[strnlen(flags, sizeof(flags)) - 1] = '\0'; | |
e62847c5 | 197 | |
2508eaa7 AD |
198 | printf(ext_fmt, cur_ex, logical_width, logical_blk, |
199 | logical_width, logical_blk + ext_blks, | |
200 | physical_width, physical_blk, | |
201 | physical_width, physical_blk + ext_blks, | |
202 | ext_len, flags); | |
e62847c5 KS |
203 | } |
204 | ||
2508eaa7 AD |
205 | static int filefrag_fiemap(int fd, int blk_shift, int *num_extents, |
206 | ext2fs_struct_stat *st) | |
e62847c5 | 207 | { |
59707c1b | 208 | __u64 buf[2048]; /* __u64 for proper field alignment */ |
e62847c5 KS |
209 | struct fiemap *fiemap = (struct fiemap *)buf; |
210 | struct fiemap_extent *fm_ext = &fiemap->fm_extents[0]; | |
84244683 | 211 | struct fiemap_extent fm_last = {0}; |
e62847c5 KS |
212 | int count = (sizeof(buf) - sizeof(*fiemap)) / |
213 | sizeof(struct fiemap_extent); | |
9c58eaf7 | 214 | unsigned long long expected = 0; |
84244683 | 215 | unsigned long long expected_dense = 0; |
e62847c5 | 216 | unsigned long flags = 0; |
9d4bade4 | 217 | unsigned int i; |
334cfccb | 218 | int fiemap_header_printed = 0; |
036fda6d | 219 | int tot_extents = 0, n = 0; |
9d4bade4 TT |
220 | int last = 0; |
221 | int rc; | |
e62847c5 | 222 | |
e62847c5 KS |
223 | memset(fiemap, 0, sizeof(struct fiemap)); |
224 | ||
e62847c5 KS |
225 | if (sync_file) |
226 | flags |= FIEMAP_FLAG_SYNC; | |
227 | ||
228 | if (xattr_map) | |
229 | flags |= FIEMAP_FLAG_XATTR; | |
230 | ||
e62847c5 KS |
231 | do { |
232 | fiemap->fm_length = ~0ULL; | |
233 | fiemap->fm_flags = flags; | |
234 | fiemap->fm_extent_count = count; | |
235 | rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap); | |
e78968f7 | 236 | if (rc < 0) { |
29758d2f | 237 | static int fiemap_incompat_printed; |
af7dbe3a | 238 | |
29758d2f AD |
239 | rc = -errno; |
240 | if (rc == -EBADR && !fiemap_incompat_printed) { | |
af7dbe3a AD |
241 | fprintf(stderr, "FIEMAP failed with unknown " |
242 | "flags %x\n", | |
29758d2f | 243 | fiemap->fm_flags); |
e62847c5 KS |
244 | fiemap_incompat_printed = 1; |
245 | } | |
e62847c5 | 246 | return rc; |
e78968f7 | 247 | } |
e62847c5 | 248 | |
036fda6d LB |
249 | /* If 0 extents are returned, then more ioctls are not needed */ |
250 | if (fiemap->fm_mapped_extents == 0) | |
251 | break; | |
252 | ||
334cfccb | 253 | if (verbose && !fiemap_header_printed) { |
2508eaa7 | 254 | print_extent_header(); |
334cfccb ES |
255 | fiemap_header_printed = 1; |
256 | } | |
257 | ||
e62847c5 | 258 | for (i = 0; i < fiemap->fm_mapped_extents; i++) { |
84244683 DW |
259 | expected_dense = fm_last.fe_physical + |
260 | fm_last.fe_length; | |
261 | expected = fm_last.fe_physical + | |
262 | fm_ext[i].fe_logical - fm_last.fe_logical; | |
2508eaa7 | 263 | if (fm_ext[i].fe_logical != 0 && |
84244683 DW |
264 | fm_ext[i].fe_physical != expected && |
265 | fm_ext[i].fe_physical != expected_dense) { | |
e62847c5 | 266 | tot_extents++; |
036fda6d | 267 | } else { |
9c58eaf7 | 268 | expected = 0; |
036fda6d LB |
269 | if (!tot_extents) |
270 | tot_extents = 1; | |
271 | } | |
a00be17e | 272 | if (verbose) |
9c58eaf7 | 273 | print_extent_info(&fm_ext[i], n, expected, |
2508eaa7 | 274 | blk_shift, st); |
e62847c5 KS |
275 | if (fm_ext[i].fe_flags & FIEMAP_EXTENT_LAST) |
276 | last = 1; | |
84244683 | 277 | fm_last = fm_ext[i]; |
e62847c5 KS |
278 | n++; |
279 | } | |
280 | ||
2508eaa7 AD |
281 | fiemap->fm_start = (fm_ext[i - 1].fe_logical + |
282 | fm_ext[i - 1].fe_length); | |
e62847c5 KS |
283 | } while (last == 0); |
284 | ||
285 | *num_extents = tot_extents; | |
00eb0eee | 286 | |
e62847c5 | 287 | return 0; |
96424130 TT |
288 | } |
289 | ||
290 | #define EXT2_DIRECT 12 | |
291 | ||
2508eaa7 AD |
292 | static int filefrag_fibmap(int fd, int blk_shift, int *num_extents, |
293 | ext2fs_struct_stat *st, | |
294 | unsigned long numblocks, int is_ext2) | |
295 | { | |
84244683 | 296 | struct fiemap_extent fm_ext, fm_last; |
2508eaa7 | 297 | unsigned long i, last_block; |
84244683 | 298 | unsigned long long logical, expected = 0; |
2508eaa7 AD |
299 | /* Blocks per indirect block */ |
300 | const long bpib = st->st_blksize / 4; | |
301 | int count; | |
302 | ||
f008143c | 303 | memset(&fm_ext, 0, sizeof(fm_ext)); |
84244683 | 304 | memset(&fm_last, 0, sizeof(fm_last)); |
2508eaa7 | 305 | if (force_extent) { |
2508eaa7 AD |
306 | fm_ext.fe_flags = FIEMAP_EXTENT_MERGED; |
307 | } | |
308 | ||
a691f8d8 LC |
309 | if (sync_file && fsync(fd) != 0) |
310 | return -errno; | |
2508eaa7 AD |
311 | |
312 | for (i = 0, logical = 0, *num_extents = 0, count = last_block = 0; | |
313 | i < numblocks; | |
314 | i++, logical += st->st_blksize) { | |
315 | unsigned long block = 0; | |
316 | int rc; | |
317 | ||
318 | if (is_ext2 && last_block) { | |
319 | if (((i - EXT2_DIRECT) % bpib) == 0) | |
320 | last_block++; | |
321 | if (((i - EXT2_DIRECT - bpib) % (bpib * bpib)) == 0) | |
322 | last_block++; | |
323 | if (((i - EXT2_DIRECT - bpib - bpib * bpib) % | |
324 | (((unsigned long long)bpib) * bpib * bpib)) == 0) | |
325 | last_block++; | |
326 | } | |
327 | rc = get_bmap(fd, i, &block); | |
328 | if (rc < 0) | |
329 | return rc; | |
330 | if (block == 0) | |
331 | continue; | |
84244683 DW |
332 | |
333 | if (*num_extents == 0 || block != last_block + 1 || | |
334 | fm_ext.fe_logical + fm_ext.fe_length != logical) { | |
335 | /* | |
336 | * This is the start of a new extent; figure out where | |
337 | * we expected it to be and report the extent. | |
338 | */ | |
339 | if (*num_extents != 0 && fm_last.fe_length) { | |
340 | expected = fm_last.fe_physical + | |
341 | (fm_ext.fe_logical - fm_last.fe_logical); | |
342 | if (expected == fm_ext.fe_physical) | |
343 | expected = 0; | |
344 | } | |
345 | if (force_extent && *num_extents == 0) | |
2508eaa7 | 346 | print_extent_header(); |
84244683 DW |
347 | if (force_extent && *num_extents != 0) { |
348 | print_extent_info(&fm_ext, *num_extents - 1, | |
349 | expected, blk_shift, st); | |
2508eaa7 | 350 | } |
84244683 DW |
351 | if (verbose && expected != 0) { |
352 | printf("Discontinuity: Block %llu is at %llu " | |
353 | "(was %llu)\n", | |
354 | fm_ext.fe_logical / st->st_blksize, | |
355 | fm_ext.fe_physical / st->st_blksize, | |
356 | expected / st->st_blksize); | |
357 | } | |
358 | /* create the new extent */ | |
359 | fm_last = fm_ext; | |
2508eaa7 | 360 | (*num_extents)++; |
f008143c | 361 | fm_ext.fe_physical = block * st->st_blksize; |
f008143c | 362 | fm_ext.fe_logical = logical; |
84244683 | 363 | fm_ext.fe_length = 0; |
2508eaa7 AD |
364 | } |
365 | fm_ext.fe_length += st->st_blksize; | |
366 | last_block = block; | |
367 | } | |
84244683 DW |
368 | if (force_extent && *num_extents != 0) { |
369 | if (fm_last.fe_length) { | |
370 | expected = fm_last.fe_physical + | |
371 | (fm_ext.fe_logical - fm_last.fe_logical); | |
372 | if (expected == fm_ext.fe_physical) | |
373 | expected = 0; | |
374 | } | |
375 | print_extent_info(&fm_ext, *num_extents - 1, expected, | |
376 | blk_shift, st); | |
377 | } | |
2508eaa7 AD |
378 | |
379 | return count; | |
380 | } | |
381 | ||
29758d2f | 382 | static int frag_report(const char *filename) |
96424130 | 383 | { |
2508eaa7 | 384 | static struct statfs fsinfo; |
01824c9b | 385 | static unsigned int blksize; |
2508eaa7 AD |
386 | ext2fs_struct_stat st; |
387 | int blk_shift; | |
642935c0 | 388 | long fd; |
7133644e | 389 | unsigned long long numblocks; |
2508eaa7 AD |
390 | int data_blocks_per_cyl = 1; |
391 | int num_extents = 1, expected = ~0; | |
96424130 | 392 | int is_ext2 = 0; |
2508eaa7 | 393 | static dev_t last_device; |
2508eaa7 | 394 | int width; |
29758d2f | 395 | int rc = 0; |
e62847c5 | 396 | |
2508eaa7 | 397 | #if defined(HAVE_OPEN64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED) |
e62847c5 KS |
398 | fd = open64(filename, O_RDONLY); |
399 | #else | |
400 | fd = open(filename, O_RDONLY); | |
401 | #endif | |
402 | if (fd < 0) { | |
29758d2f | 403 | rc = -errno; |
e62847c5 | 404 | perror("open"); |
29758d2f | 405 | return rc; |
e62847c5 | 406 | } |
96424130 | 407 | |
2508eaa7 AD |
408 | #if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED) |
409 | if (fstat64(fd, &st) < 0) { | |
9c07dc00 | 410 | #else |
2508eaa7 | 411 | if (fstat(fd, &st) < 0) { |
9c07dc00 | 412 | #endif |
29758d2f | 413 | rc = -errno; |
96424130 | 414 | perror("stat"); |
01824c9b | 415 | goto out_close; |
96424130 | 416 | } |
2508eaa7 AD |
417 | |
418 | if (last_device != st.st_dev) { | |
419 | if (fstatfs(fd, &fsinfo) < 0) { | |
f2ca701c | 420 | rc = -errno; |
2508eaa7 | 421 | perror("fstatfs"); |
01824c9b | 422 | goto out_close; |
2508eaa7 | 423 | } |
01824c9b RP |
424 | if (ioctl(fd, FIGETBSZ, &blksize) < 0) |
425 | blksize = fsinfo.f_bsize; | |
2508eaa7 AD |
426 | if (verbose) |
427 | printf("Filesystem type is: %lx\n", | |
29758d2f | 428 | (unsigned long)fsinfo.f_type); |
2508eaa7 | 429 | } |
01824c9b | 430 | st.st_blksize = blksize; |
af7dbe3a AD |
431 | if (fsinfo.f_type == 0xef51 || fsinfo.f_type == 0xef52 || |
432 | fsinfo.f_type == 0xef53) { | |
433 | unsigned int flags; | |
434 | ||
435 | if (ioctl(fd, EXT3_IOC_GETFLAGS, &flags) == 0 && | |
436 | !(flags & EXT4_EXTENTS_FL)) | |
437 | is_ext2 = 1; | |
438 | } | |
2508eaa7 AD |
439 | |
440 | if (is_ext2) { | |
01824c9b | 441 | long cylgroups = div_ceil(fsinfo.f_blocks, blksize * 8); |
2508eaa7 AD |
442 | |
443 | if (verbose && last_device != st.st_dev) | |
444 | printf("Filesystem cylinder groups approximately %ld\n", | |
445 | cylgroups); | |
446 | ||
01824c9b | 447 | data_blocks_per_cyl = blksize * 8 - |
2508eaa7 | 448 | (fsinfo.f_files / 8 / cylgroups) - 3; |
96424130 | 449 | } |
2508eaa7 AD |
450 | last_device = st.st_dev; |
451 | ||
452 | width = int_log10(fsinfo.f_blocks); | |
453 | if (width > physical_width) | |
454 | physical_width = width; | |
e62847c5 | 455 | |
01824c9b | 456 | numblocks = (st.st_size + blksize - 1) / blksize; |
2508eaa7 AD |
457 | if (blocksize != 0) |
458 | blk_shift = int_log2(blocksize); | |
459 | else | |
01824c9b | 460 | blk_shift = int_log2(blksize); |
e62847c5 | 461 | |
2508eaa7 AD |
462 | width = int_log10(numblocks); |
463 | if (width > logical_width) | |
464 | logical_width = width; | |
e62847c5 | 465 | if (verbose) |
7133644e | 466 | printf("File size of %s is %llu (%llu block%s of %d bytes)\n", |
2508eaa7 | 467 | filename, (unsigned long long)st.st_size, |
01824c9b | 468 | numblocks * blksize >> blk_shift, |
2508eaa7 AD |
469 | numblocks == 1 ? "" : "s", 1 << blk_shift); |
470 | ||
29758d2f AD |
471 | if (!force_bmap) { |
472 | rc = filefrag_fiemap(fd, blk_shift, &num_extents, &st); | |
473 | expected = 0; | |
474 | } | |
475 | ||
af7dbe3a | 476 | if (force_bmap || rc < 0) { /* FIEMAP failed, try FIBMAP instead */ |
2508eaa7 AD |
477 | expected = filefrag_fibmap(fd, blk_shift, &num_extents, |
478 | &st, numblocks, is_ext2); | |
479 | if (expected < 0) { | |
29758d2f | 480 | if (expected == -EINVAL || expected == -ENOTTY) { |
2508eaa7 AD |
481 | fprintf(stderr, "%s: FIBMAP unsupported\n", |
482 | filename); | |
29758d2f AD |
483 | } else if (expected == -EPERM) { |
484 | fprintf(stderr, | |
485 | "%s: FIBMAP requires root privileges\n", | |
486 | filename); | |
487 | } else { | |
2508eaa7 | 488 | fprintf(stderr, "%s: FIBMAP error: %s", |
29758d2f | 489 | filename, strerror(expected)); |
e62847c5 | 490 | } |
29758d2f | 491 | rc = expected; |
2508eaa7 | 492 | goto out_close; |
29758d2f AD |
493 | } else { |
494 | rc = 0; | |
96424130 | 495 | } |
2508eaa7 | 496 | expected = expected / data_blocks_per_cyl + 1; |
96424130 | 497 | } |
2508eaa7 | 498 | |
e62847c5 | 499 | if (num_extents == 1) |
96424130 TT |
500 | printf("%s: 1 extent found", filename); |
501 | else | |
e62847c5 | 502 | printf("%s: %d extents found", filename, num_extents); |
1e003cc7 | 503 | /* count, and thus expected, only set for indirect FIBMAP'd files */ |
2508eaa7 AD |
504 | if (is_ext2 && expected && expected < num_extents) |
505 | printf(", perfection would be %d extent%s\n", expected, | |
506 | (expected > 1) ? "s" : ""); | |
507 | else | |
96424130 | 508 | fputc('\n', stdout); |
2508eaa7 | 509 | out_close: |
9290404e | 510 | close(fd); |
29758d2f AD |
511 | |
512 | return rc; | |
96424130 TT |
513 | } |
514 | ||
6b394c1c | 515 | static void usage(const char *progname) |
96424130 | 516 | { |
f9b16db8 | 517 | fprintf(stderr, "Usage: %s [-b{blocksize}] [-BeksvxX] file ...\n", |
2508eaa7 | 518 | progname); |
96424130 TT |
519 | exit(1); |
520 | } | |
521 | ||
522 | int main(int argc, char**argv) | |
523 | { | |
524 | char **cpp; | |
29758d2f | 525 | int rc = 0, c; |
96424130 | 526 | |
af7dbe3a | 527 | while ((c = getopt(argc, argv, "Bb::eksvxX")) != EOF) { |
96424130 | 528 | switch (c) { |
5d5e01d7 TT |
529 | case 'B': |
530 | force_bmap++; | |
531 | break; | |
e62847c5 | 532 | case 'b': |
2508eaa7 AD |
533 | if (optarg) { |
534 | char *end; | |
535 | blocksize = strtoul(optarg, &end, 0); | |
536 | if (end) { | |
537 | switch (end[0]) { | |
538 | case 'g': | |
539 | case 'G': | |
540 | blocksize *= 1024; | |
541 | /* no break */ | |
542 | case 'm': | |
543 | case 'M': | |
544 | blocksize *= 1024; | |
545 | /* no break */ | |
546 | case 'k': | |
547 | case 'K': | |
548 | blocksize *= 1024; | |
549 | break; | |
550 | default: | |
551 | break; | |
552 | } | |
553 | } | |
554 | } else { /* Allow -b without argument for compat. Remove | |
555 | * this eventually so "-b {blocksize}" works */ | |
556 | fprintf(stderr, "%s: -b needs a blocksize " | |
557 | "option, assuming 1024-byte blocks.\n", | |
558 | argv[0]); | |
559 | blocksize = 1024; | |
560 | } | |
e62847c5 | 561 | break; |
2508eaa7 AD |
562 | case 'e': |
563 | force_extent++; | |
564 | if (!verbose) | |
565 | verbose++; | |
566 | break; | |
567 | case 'k': | |
568 | blocksize = 1024; | |
96424130 | 569 | break; |
e62847c5 KS |
570 | case 's': |
571 | sync_file++; | |
572 | break; | |
2508eaa7 AD |
573 | case 'v': |
574 | verbose++; | |
575 | break; | |
e62847c5 KS |
576 | case 'x': |
577 | xattr_map++; | |
578 | break; | |
2508eaa7 AD |
579 | case 'X': |
580 | ext_fmt = hex_fmt; | |
581 | break; | |
96424130 TT |
582 | default: |
583 | usage(argv[0]); | |
584 | break; | |
585 | } | |
af7dbe3a | 586 | } |
29758d2f | 587 | |
96424130 TT |
588 | if (optind == argc) |
589 | usage(argv[0]); | |
29758d2f | 590 | |
7bfa94ad | 591 | for (cpp = argv + optind; *cpp != NULL; cpp++) { |
29758d2f AD |
592 | int rc2 = frag_report(*cpp); |
593 | ||
594 | if (rc2 < 0 && rc == 0) | |
f2ca701c | 595 | rc = rc2; |
29758d2f AD |
596 | } |
597 | ||
af7dbe3a | 598 | return -rc; |
96424130 | 599 | } |
b34cbddb | 600 | #endif |