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