/* * Copyright (c) 2008-2009 Eric Sandeen * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License V2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include static void usage(void) { printf("Usage: fiemap [-vrSCX] [-s start] [-l length] [-c buf count] [-m max] filename\n"); printf(" -v : verbose mode (can be used multiple times)\n"); printf(" -r : raw output: print raw ioctl structure values\n"); printf(" -S : sync file before mapping\n"); printf(" -C : get only get extent count, not mapping\n"); printf(" -X : report xattr mapping\n"); printf(" -s start : start of mapping in bytes (default 0)\n"); printf(" -l length : length of mapping in bytes (default to end of file)\n"); printf(" -c count : count of extents in ioctl input structure (default 32)\n"); printf(" -m max : max nr of ioctls to call before exit (default 512)\n"); exit(EXIT_FAILURE); } #define EXABYTES(x) ((long long)(x) << 60) #define PETABYTES(x) ((long long)(x) << 50) #define TERABYTES(x) ((long long)(x) << 40) #define GIGABYTES(x) ((long long)(x) << 30) #define MEGABYTES(x) ((long long)(x) << 20) #define KILOBYTES(x) ((long long)(x) << 10) static long long cvtnum(char *s) { long long i; char *sp; int c; i = strtoll(s, &sp, 0); if (i == 0 && sp == s) return -1LL; if (*sp == '\0') return i; if (sp[1] != '\0') return -1LL; c = tolower(*sp); switch (c) { case 'k': return KILOBYTES(i); case 'm': return MEGABYTES(i); case 'g': return GIGABYTES(i); case 't': return TERABYTES(i); case 'p': return PETABYTES(i); case 'e': return EXABYTES(i); } return -1LL; } static void show_extent_block(struct fiemap_extent *extent, int extnum, int blocksize) { __u64 logical = extent->fe_logical; __u64 phys = extent->fe_physical; __u64 length = extent->fe_length; int flags = extent->fe_flags; printf("ext: %3u logical: [%8llu..%8llu] phys: %8llu..%8llu " "flags: 0x%03X tot: %llu\n", extnum, logical / blocksize, (logical + length - 1) / blocksize, phys / blocksize, (phys + length - 1) / blocksize, flags, (length / blocksize)); } static void show_extent_raw(struct fiemap_extent *extent, int extnum) { printf("Extent %3u: logical: %10lld physical: %10lld " "flags 0x%03X length: %10lld\n", extnum, extent->fe_logical, extent->fe_physical, extent->fe_flags, extent->fe_length); } static void show_extents(struct fiemap *fiemap, uint *start_extent, int blocksize, int raw, int *is_last) { unsigned int i; for (i = 0; i < fiemap->fm_mapped_extents; i++) { if (raw) show_extent_raw(&fiemap->fm_extents[i], *start_extent); else show_extent_block(&fiemap->fm_extents[i], *start_extent, blocksize); if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) { *is_last = 1; break; } (*start_extent)++; } /* XXX ? */ // if (fiemap->fm_mapped_extents < fiemap->fm_extent_count) // *is_last = 1; } int main(int argc, char **argv) { int blocksize = 0; /* filesystem blocksize */ uint count = 32; /* extent count */ int fd; /* file descriptor */ int last = 0; /* last extent found */ int xattr = 0; /* return xattr mapping */ int maxioctls = 512; /* max ioctls to try */ int opt; int rc; int raw = 0; /* raw output format */ int sync = 0; /* sync file before mapping */ int verbose = 0; /* verbose output */ char *fname; /* filename to map */ char *fiebuf; /* fiemap buffer / ioctl argument */ __u64 lstart = 0; /* initial logical input mapping start */ __u64 llength = ~0ULL;/* initial logical input mapping length */ uint start_ext = 0; /* starting extent nr. for this batch */ __u64 next_start = 0; /* logical start for loop */ __u32 flags = 0; struct fiemap *fiemap; /* XXX er, can llength be ~0ULL if start > 0? */ /* no, we'll wrap. crap. */ while ((opt = getopt(argc, argv, "s:l:c:m:rSCXv")) != -1) { switch(opt) { /* logical mapping offset */ case 's': lstart = cvtnum(optarg); break; /* logical mapping length */ case 'l': llength = cvtnum(optarg); break; /* count of extent buffers to send */ case 'c': count = atoi(optarg); break; /* max nr. of ioctls to try (safety net) */ case 'm': maxioctls = atoi(optarg); break; /* raw format output */ case 'r': raw++; break; /* sync file before mapping */ case 'S': sync++; break; /* return extents for xattrs */ case 'X': xattr++; break; /* be verbose */ case 'v': verbose++; break; default: usage(); } } fname = argv[optind++]; if (!fname) usage(); /* Set the flags for the header */ if (sync) flags |= FIEMAP_FLAG_SYNC; if (xattr) flags |= FIEMAP_FLAG_XATTR; /* Don't want lstart +llength to wrap */ if (lstart && llength == ~0ULL) llength -= lstart; /* The whole buffer, extent maps and all */ fiebuf = malloc(sizeof(struct fiemap) + (count * sizeof(struct fiemap_extent))); if (!fiebuf) { perror("Could not allocate fiemap buffers"); exit(1); } fd = open(fname, O_RDONLY); if (fd < 0) { perror("Can't open file"); exit(1); } if (ioctl(fd, FIGETBSZ, &blocksize) < 0) { perror("Can't get filesystem block size"); close(fd); exit(1); } if (verbose) { if (!raw) printf("Printing values in %d-byte-block units\n", blocksize); else printf("Printing values in byte units\n"); if (lstart || llength != ~0ULL || verbose > 1) printf("Mapping %llu bytes from offset %llu\n", llength, lstart); } /* set up the header */ fiemap = (struct fiemap *)fiebuf; fiemap->fm_flags = flags; fiemap->fm_extent_count = count; fiemap->fm_mapped_extents = 0; /* output only */ next_start = lstart; do { int last_map; /* Set up the next call arguments */ fiemap->fm_start = next_start; fiemap->fm_length = lstart + llength - next_start; fiemap->fm_extent_count = count; if (verbose > 1) printf("Input: start %llu length %llu " "flags 0x%X count %u\n", fiemap->fm_start, fiemap->fm_length, fiemap->fm_flags, fiemap->fm_extent_count); rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap); if (rc < 0) { perror("FIEMAP ioctl failed"); close(fd); exit(1); } if (verbose > 1) printf("Output: start %llu length %llu " "flags 0x%X count %u mapped %u\n", fiemap->fm_start, fiemap->fm_length, fiemap->fm_flags, fiemap->fm_extent_count, fiemap->fm_mapped_extents); show_extents(fiemap, &start_ext, blocksize, raw, &last); if (last) break; last_map = fiemap->fm_mapped_extents - 1; next_start = fiemap->fm_extents[last_map].fe_logical + fiemap->fm_extents[last_map].fe_length; /* need to stop if we've reached what we asked for */ maxioctls--; } while (maxioctls > 0); close(fd); return 0; }