]>
Commit | Line | Data |
---|---|---|
2bd0ea18 | 1 | /* |
da23017d NS |
2 | * Copyright (c) 2000-2005 Silicon Graphics, Inc. |
3 | * All Rights Reserved. | |
dfc130f3 | 4 | * |
da23017d NS |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public License as | |
2bd0ea18 | 7 | * published by the Free Software Foundation. |
dfc130f3 | 8 | * |
da23017d NS |
9 | * This program is distributed in the hope that it would be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
dfc130f3 | 13 | * |
da23017d NS |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write the Free Software Foundation, | |
16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
2bd0ea18 NS |
17 | */ |
18 | ||
2a1888c5 | 19 | #include <xfs/xfs.h> |
0717a7db | 20 | #include <xfs/command.h> |
e246ba5f | 21 | #include "init.h" |
48c46ee3 | 22 | #include "io.h" |
2bd0ea18 | 23 | |
e246ba5f | 24 | static cmdinfo_t bmap_cmd; |
2bd0ea18 | 25 | |
e246ba5f NS |
26 | static void |
27 | bmap_help(void) | |
2bd0ea18 | 28 | { |
e246ba5f NS |
29 | printf(_( |
30 | "\n" | |
31 | " prints the block mapping for an XFS file's data or attribute forks" | |
32 | "\n" | |
33 | " Example:\n" | |
34 | " 'bmap -vp' - tabular format verbose map, including unwritten extents\n" | |
35 | "\n" | |
36 | " bmap prints the map of disk blocks used by the current file.\n" | |
37 | " The map lists each extent used by the file, as well as regions in the\n" | |
38 | " file that do not have any corresponding blocks (holes).\n" | |
39 | " By default, each line of the listing takes the following form:\n" | |
40 | " extent: [startoffset..endoffset]: startblock..endblock\n" | |
41 | " Holes are marked by replacing the startblock..endblock with 'hole'.\n" | |
42 | " All the file offsets and disk blocks are in units of 512-byte blocks.\n" | |
43 | " -a -- prints the attribute fork map instead of the data fork.\n" | |
44 | " -d -- suppresses a DMAPI read event, offline portions shown as holes.\n" | |
45 | " -l -- also displays the length of each extent in 512-byte blocks.\n" | |
46 | " Note: the bmap for non-regular files can be obtained provided the file\n" | |
47 | " was opened appropriately (in particular, must be opened read-only).\n" | |
48 | "\n")); | |
49 | } | |
2bd0ea18 | 50 | |
e246ba5f NS |
51 | static int |
52 | numlen( | |
53 | off64_t val) | |
54 | { | |
55 | off64_t tmp; | |
56 | int len; | |
9440d84d | 57 | |
e246ba5f NS |
58 | for (len = 0, tmp = val; tmp > 0; tmp = tmp/10) |
59 | len++; | |
60 | return (len == 0 ? 1 : len); | |
61 | } | |
62 | ||
63 | int | |
64 | bmap_f( | |
65 | int argc, | |
66 | char **argv) | |
67 | { | |
68 | struct fsxattr fsx; | |
69 | struct getbmapx *map; | |
c0211f67 | 70 | struct xfs_fsop_geom fsgeo; |
e246ba5f NS |
71 | int map_size; |
72 | int loop = 0; | |
73 | int flg = 0; | |
74 | int aflag = 0; | |
75 | int lflag = 0; | |
76 | int nflag = 0; | |
77 | int vflag = 0; | |
78 | int bmv_iflags = 0; /* flags for XFS_IOC_GETBMAPX */ | |
79 | int i = 0; | |
80 | int c; | |
81 | ||
82 | while ((c = getopt(argc, argv, "adln:pv")) != EOF) { | |
83 | switch (c) { | |
84 | case 'a': /* Attribute fork. */ | |
2bd0ea18 NS |
85 | bmv_iflags |= BMV_IF_ATTRFORK; |
86 | aflag = 1; | |
87 | break; | |
e246ba5f | 88 | case 'l': /* list number of blocks with each extent */ |
2bd0ea18 NS |
89 | lflag = 1; |
90 | break; | |
e246ba5f | 91 | case 'n': /* number of extents specified */ |
2bd0ea18 NS |
92 | nflag = atoi(optarg); |
93 | break; | |
94 | case 'd': | |
95 | /* do not recall possibly offline DMAPI files */ | |
96 | bmv_iflags |= BMV_IF_NO_DMAPI_READ; | |
97 | break; | |
98 | case 'p': | |
99 | /* report unwritten preallocated blocks */ | |
100 | bmv_iflags |= BMV_IF_PREALLOC; | |
101 | break; | |
e246ba5f | 102 | case 'v': /* Verbose output */ |
2bd0ea18 NS |
103 | vflag++; |
104 | break; | |
2bd0ea18 | 105 | default: |
48c46ee3 | 106 | return command_usage(&bmap_cmd); |
2bd0ea18 NS |
107 | } |
108 | } | |
dfc130f3 | 109 | if (aflag) |
e246ba5f | 110 | bmv_iflags &= ~(BMV_IF_PREALLOC|BMV_IF_NO_DMAPI_READ); |
2bd0ea18 NS |
111 | |
112 | if (vflag) { | |
48c46ee3 NS |
113 | c = xfsctl(file->name, file->fd, XFS_IOC_FSGEOMETRY_V1, &fsgeo); |
114 | if (c < 0) { | |
9440d84d NS |
115 | fprintf(stderr, |
116 | _("%s: can't get geometry [\"%s\"]: %s\n"), | |
48c46ee3 | 117 | progname, file->name, strerror(errno)); |
e246ba5f NS |
118 | exitcode = 1; |
119 | return 0; | |
2bd0ea18 | 120 | } |
48c46ee3 NS |
121 | c = xfsctl(file->name, file->fd, XFS_IOC_FSGETXATTR, &fsx); |
122 | if (c < 0) { | |
9440d84d NS |
123 | fprintf(stderr, |
124 | _("%s: cannot read attrs on \"%s\": %s\n"), | |
48c46ee3 | 125 | progname, file->name, strerror(errno)); |
e246ba5f NS |
126 | exitcode = 1; |
127 | return 0; | |
2bd0ea18 NS |
128 | } |
129 | ||
2bd0ea18 | 130 | if (fsx.fsx_xflags == XFS_XFLAG_REALTIME) { |
dfc130f3 | 131 | /* |
2bd0ea18 NS |
132 | * ag info not applicable to rt, continue |
133 | * without ag output. | |
134 | */ | |
dfc130f3 | 135 | vflag = 0; |
2bd0ea18 NS |
136 | } |
137 | } | |
138 | ||
e246ba5f | 139 | map_size = nflag ? nflag+1 : 32; /* initial guess - 256 */ |
2bd0ea18 NS |
140 | map = malloc(map_size*sizeof(*map)); |
141 | if (map == NULL) { | |
9440d84d | 142 | fprintf(stderr, _("%s: malloc of %d bytes failed.\n"), |
c03d02f8 | 143 | progname, (int)(map_size * sizeof(*map))); |
e246ba5f NS |
144 | exitcode = 1; |
145 | return 0; | |
2bd0ea18 | 146 | } |
dfc130f3 | 147 | |
2bd0ea18 | 148 | |
93d9f139 NS |
149 | /* Try the xfsctl(XFS_IOC_GETBMAPX) for the number of extents specified |
150 | * by nflag, or the initial guess number of extents (256). | |
2bd0ea18 | 151 | * |
93d9f139 | 152 | * If there are more extents than we guessed, use xfsctl |
dfc130f3 | 153 | * (XFS_IOC_FSGETXATTR[A]) to get the extent count, realloc some more |
2bd0ea18 NS |
154 | * space based on this count, and try again. |
155 | * | |
156 | * If the initial FGETBMAPX attempt returns EINVAL, this may mean | |
157 | * that we tried the FGETBMAPX on a zero length file. If we get | |
158 | * EINVAL, check the length with fstat() and return "no extents" | |
159 | * if the length == 0. | |
160 | * | |
93d9f139 | 161 | * Why not do the xfsctl(XFS_IOC_FSGETXATTR[A]) first? Two reasons: |
2bd0ea18 NS |
162 | * (1) The extent count may be wrong for a file with delayed |
163 | * allocation blocks. The XFS_IOC_GETBMAPX forces the real | |
164 | * allocation and fixes up the extent count. | |
dfc130f3 RC |
165 | * (2) For XFS_IOC_GETBMAP[X] on a DMAPI file that has been moved |
166 | * offline by a DMAPI application (e.g., DMF) the | |
2bd0ea18 NS |
167 | * XFS_IOC_FSGETXATTR only reflects the extents actually online. |
168 | * Doing XFS_IOC_GETBMAPX call first forces that data blocks online | |
169 | * and then everything proceeds normally (see PV #545725). | |
dfc130f3 | 170 | * |
2bd0ea18 NS |
171 | * If you don't want this behavior on a DMAPI offline file, |
172 | * try the "-d" option which sets the BMV_IF_NO_DMAPI_READ | |
173 | * iflag for XFS_IOC_GETBMAPX. | |
174 | */ | |
175 | ||
176 | do { /* loop a miximum of two times */ | |
177 | ||
178 | bzero(map, sizeof(*map)); /* zero header */ | |
179 | ||
180 | map->bmv_length = -1; | |
181 | map->bmv_count = map_size; | |
182 | map->bmv_iflags = bmv_iflags; | |
183 | ||
48c46ee3 | 184 | i = xfsctl(file->name, file->fd, XFS_IOC_GETBMAPX, map); |
2bd0ea18 NS |
185 | if (i < 0) { |
186 | if ( errno == EINVAL | |
e246ba5f | 187 | && !aflag && filesize() == 0) { |
2bd0ea18 NS |
188 | break; |
189 | } else { | |
93d9f139 NS |
190 | fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETBMAPX)" |
191 | " iflags=0x%x [\"%s\"]: %s\n"), | |
48c46ee3 | 192 | progname, map->bmv_iflags, file->name, |
c03d02f8 | 193 | strerror(errno)); |
2bd0ea18 | 194 | free(map); |
e246ba5f NS |
195 | exitcode = 1; |
196 | return 0; | |
2bd0ea18 NS |
197 | } |
198 | } | |
199 | if (nflag) | |
200 | break; | |
201 | if (map->bmv_entries < map->bmv_count-1) | |
202 | break; | |
93d9f139 | 203 | /* Get number of extents from xfsctl XFS_IOC_FSGETXATTR[A] |
2bd0ea18 NS |
204 | * syscall. |
205 | */ | |
48c46ee3 | 206 | i = xfsctl(file->name, file->fd, aflag ? |
e246ba5f | 207 | XFS_IOC_FSGETXATTRA : XFS_IOC_FSGETXATTR, &fsx); |
2bd0ea18 | 208 | if (i < 0) { |
93d9f139 NS |
209 | fprintf(stderr, "%s: xfsctl(XFS_IOC_FSGETXATTR%s) " |
210 | "[\"%s\"]: %s\n", progname, aflag ? "A" : "", | |
48c46ee3 | 211 | file->name, strerror(errno)); |
2bd0ea18 | 212 | free(map); |
e246ba5f NS |
213 | exitcode = 1; |
214 | return 0; | |
2bd0ea18 NS |
215 | } |
216 | if (fsx.fsx_nextents >= map_size-1) { | |
217 | map_size = 2*(fsx.fsx_nextents+1); | |
218 | map = realloc(map, map_size*sizeof(*map)); | |
219 | if (map == NULL) { | |
9440d84d NS |
220 | fprintf(stderr, |
221 | _("%s: cannot realloc %d bytes\n"), | |
c03d02f8 | 222 | progname, (int)(map_size*sizeof(*map))); |
e246ba5f NS |
223 | exitcode = 1; |
224 | return 0; | |
2bd0ea18 NS |
225 | } |
226 | } | |
227 | } while (++loop < 2); | |
228 | if (!nflag) { | |
229 | if (map->bmv_entries <= 0) { | |
48c46ee3 | 230 | printf(_("%s: no extents\n"), file->name); |
2bd0ea18 NS |
231 | free(map); |
232 | return 0; | |
233 | } | |
234 | } | |
48c46ee3 | 235 | printf("%s:\n", file->name); |
2bd0ea18 NS |
236 | if (!vflag) { |
237 | for (i = 0; i < map->bmv_entries; i++) { | |
238 | printf("\t%d: [%lld..%lld]: ", i, | |
5b64e00a | 239 | (long long) map[i + 1].bmv_offset, |
dfc130f3 | 240 | (long long)(map[i + 1].bmv_offset + |
5b64e00a | 241 | map[i + 1].bmv_length - 1LL)); |
2bd0ea18 | 242 | if (map[i + 1].bmv_block == -1) |
9440d84d | 243 | printf(_("hole")); |
2bd0ea18 | 244 | else { |
5b64e00a NS |
245 | printf("%lld..%lld", |
246 | (long long) map[i + 1].bmv_block, | |
247 | (long long)(map[i + 1].bmv_block + | |
248 | map[i + 1].bmv_length - 1LL)); | |
2bd0ea18 NS |
249 | |
250 | } | |
251 | if (lflag) | |
9440d84d NS |
252 | printf(_(" %lld blocks\n"), |
253 | (long long)map[i+1].bmv_length); | |
2bd0ea18 NS |
254 | else |
255 | printf("\n"); | |
256 | } | |
257 | } else { | |
258 | /* | |
dfc130f3 | 259 | * Verbose mode displays: |
2bd0ea18 | 260 | * extent: [startoffset..endoffset]: startblock..endblock \ |
dfc130f3 | 261 | * ag# (agoffset..agendoffset) totalbbs |
2bd0ea18 NS |
262 | */ |
263 | #define MINRANGE_WIDTH 16 | |
264 | #define MINAG_WIDTH 2 | |
265 | #define MINTOT_WIDTH 5 | |
d371bee3 NS |
266 | #define NFLG 5 /* count of flags */ |
267 | #define FLG_NULL 000000 /* Null flag */ | |
268 | #define FLG_PRE 010000 /* Unwritten extent */ | |
269 | #define FLG_BSU 001000 /* Not on begin of stripe unit */ | |
270 | #define FLG_ESU 000100 /* Not on end of stripe unit */ | |
271 | #define FLG_BSW 000010 /* Not on begin of stripe width */ | |
272 | #define FLG_ESW 000001 /* Not on end of stripe width */ | |
4ca431fc NS |
273 | int agno; |
274 | off64_t agoff, bbperag; | |
dfc130f3 RC |
275 | int foff_w, boff_w, aoff_w, tot_w, agno_w; |
276 | char rbuf[32], bbuf[32], abuf[32]; | |
4ca431fc | 277 | int sunit, swidth; |
2bd0ea18 NS |
278 | |
279 | foff_w = boff_w = aoff_w = MINRANGE_WIDTH; | |
280 | tot_w = MINTOT_WIDTH; | |
dfc130f3 | 281 | bbperag = (off64_t)fsgeo.agblocks * |
e246ba5f | 282 | (off64_t)fsgeo.blocksize / BBSIZE; |
4595c940 JK |
283 | sunit = (fsgeo.sunit * fsgeo.blocksize) / BBSIZE; |
284 | swidth = (fsgeo.swidth * fsgeo.blocksize) / BBSIZE; | |
d371bee3 | 285 | flg = sunit; |
2bd0ea18 | 286 | |
dfc130f3 | 287 | /* |
2bd0ea18 NS |
288 | * Go through the extents and figure out the width |
289 | * needed for all columns. | |
290 | */ | |
291 | for (i = 0; i < map->bmv_entries; i++) { | |
dfc130f3 | 292 | snprintf(rbuf, sizeof(rbuf), "[%lld..%lld]:", |
5b64e00a NS |
293 | (long long) map[i + 1].bmv_offset, |
294 | (long long)(map[i + 1].bmv_offset + | |
295 | map[i + 1].bmv_length - 1LL)); | |
d371bee3 NS |
296 | if (map[i + 1].bmv_oflags & BMV_OF_PREALLOC) |
297 | flg = 1; | |
2bd0ea18 | 298 | if (map[i + 1].bmv_block == -1) { |
dfc130f3 RC |
299 | foff_w = max(foff_w, strlen(rbuf)); |
300 | tot_w = max(tot_w, | |
2bd0ea18 NS |
301 | numlen(map[i+1].bmv_length)); |
302 | } else { | |
dfc130f3 | 303 | snprintf(bbuf, sizeof(bbuf), "%lld..%lld", |
5b64e00a NS |
304 | (long long) map[i + 1].bmv_block, |
305 | (long long)(map[i + 1].bmv_block + | |
306 | map[i + 1].bmv_length - 1LL)); | |
2bd0ea18 NS |
307 | agno = map[i + 1].bmv_block / bbperag; |
308 | agoff = map[i + 1].bmv_block - (agno * bbperag); | |
dfc130f3 | 309 | snprintf(abuf, sizeof(abuf), "(%lld..%lld)", |
5b64e00a | 310 | (long long)agoff, (long long) |
2bd0ea18 | 311 | (agoff + map[i + 1].bmv_length - 1LL)); |
dfc130f3 RC |
312 | foff_w = max(foff_w, strlen(rbuf)); |
313 | boff_w = max(boff_w, strlen(bbuf)); | |
314 | aoff_w = max(aoff_w, strlen(abuf)); | |
315 | tot_w = max(tot_w, | |
2bd0ea18 NS |
316 | numlen(map[i+1].bmv_length)); |
317 | } | |
318 | } | |
319 | agno_w = max(MINAG_WIDTH, numlen(fsgeo.agcount)); | |
d371bee3 | 320 | printf("%4s: %-*s %-*s %*s %-*s %*s%s\n", |
9440d84d NS |
321 | _("EXT"), |
322 | foff_w, _("FILE-OFFSET"), | |
323 | boff_w, _("BLOCK-RANGE"), | |
324 | agno_w, _("AG"), | |
325 | aoff_w, _("AG-OFFSET"), | |
d371bee3 NS |
326 | tot_w, _("TOTAL"), |
327 | flg ? _(" FLAGS") : ""); | |
2bd0ea18 | 328 | for (i = 0; i < map->bmv_entries; i++) { |
d371bee3 NS |
329 | flg = FLG_NULL; |
330 | if (map[i + 1].bmv_oflags & BMV_OF_PREALLOC) { | |
331 | flg |= FLG_PRE; | |
332 | } | |
4595c940 JK |
333 | /* |
334 | * If striping enabled, determine if extent starts/ends | |
335 | * on a stripe unit boundary. | |
336 | */ | |
4ca431fc | 337 | if (sunit) { |
4ca431fc NS |
338 | if (map[i + 1].bmv_block % sunit != 0) { |
339 | flg |= FLG_BSU; | |
340 | } | |
dfc130f3 | 341 | if (((map[i + 1].bmv_block + |
4ca431fc NS |
342 | map[i + 1].bmv_length ) % sunit ) != 0) { |
343 | flg |= FLG_ESU; | |
344 | } | |
345 | if (map[i + 1].bmv_block % swidth != 0) { | |
346 | flg |= FLG_BSW; | |
347 | } | |
dfc130f3 | 348 | if (((map[i + 1].bmv_block + |
4ca431fc NS |
349 | map[i + 1].bmv_length ) % swidth ) != 0) { |
350 | flg |= FLG_ESW; | |
351 | } | |
352 | } | |
dfc130f3 | 353 | snprintf(rbuf, sizeof(rbuf), "[%lld..%lld]:", |
5b64e00a NS |
354 | (long long) map[i + 1].bmv_offset, |
355 | (long long)(map[i + 1].bmv_offset + | |
356 | map[i + 1].bmv_length - 1LL)); | |
2bd0ea18 | 357 | if (map[i + 1].bmv_block == -1) { |
dfc130f3 RC |
358 | printf("%4d: %-*s %-*s %*s %-*s %*lld\n", |
359 | i, | |
360 | foff_w, rbuf, | |
9440d84d | 361 | boff_w, _("hole"), |
2bd0ea18 | 362 | agno_w, "", |
dfc130f3 | 363 | aoff_w, "", |
5b64e00a | 364 | tot_w, (long long)map[i+1].bmv_length); |
2bd0ea18 | 365 | } else { |
dfc130f3 | 366 | snprintf(bbuf, sizeof(bbuf), "%lld..%lld", |
5b64e00a NS |
367 | (long long) map[i + 1].bmv_block, |
368 | (long long)(map[i + 1].bmv_block + | |
369 | map[i + 1].bmv_length - 1LL)); | |
2bd0ea18 NS |
370 | agno = map[i + 1].bmv_block / bbperag; |
371 | agoff = map[i + 1].bmv_block - (agno * bbperag); | |
dfc130f3 | 372 | snprintf(abuf, sizeof(abuf), "(%lld..%lld)", |
5b64e00a | 373 | (long long)agoff, (long long) |
2bd0ea18 | 374 | (agoff + map[i + 1].bmv_length - 1LL)); |
e246ba5f | 375 | printf("%4d: %-*s %-*s %*d %-*s %*lld", |
dfc130f3 RC |
376 | i, |
377 | foff_w, rbuf, | |
378 | boff_w, bbuf, | |
379 | agno_w, agno, | |
380 | aoff_w, abuf, | |
5b64e00a | 381 | tot_w, (long long)map[i+1].bmv_length); |
4ca431fc NS |
382 | if (flg == FLG_NULL) { |
383 | printf("\n"); | |
384 | } else { | |
d371bee3 | 385 | printf(" %-*.*o\n", NFLG, NFLG, flg); |
4ca431fc | 386 | } |
2bd0ea18 NS |
387 | } |
388 | } | |
d371bee3 NS |
389 | if (flg && vflag > 1) { |
390 | printf(_(" FLAG Values:\n")); | |
391 | printf(_(" %*.*o Unwritten preallocated extent\n"), | |
392 | NFLG+1, NFLG+1, FLG_PRE); | |
393 | printf(_(" %*.*o Doesn't begin on stripe unit\n"), | |
394 | NFLG+1, NFLG+1, FLG_BSU); | |
395 | printf(_(" %*.*o Doesn't end on stripe unit\n"), | |
396 | NFLG+1, NFLG+1, FLG_ESU); | |
397 | printf(_(" %*.*o Doesn't begin on stripe width\n"), | |
398 | NFLG+1, NFLG+1, FLG_BSW); | |
399 | printf(_(" %*.*o Doesn't end on stripe width\n"), | |
400 | NFLG+1, NFLG+1, FLG_ESW); | |
4ca431fc | 401 | } |
2bd0ea18 NS |
402 | } |
403 | free(map); | |
404 | return 0; | |
405 | } | |
406 | ||
e246ba5f NS |
407 | void |
408 | bmap_init(void) | |
2bd0ea18 | 409 | { |
e246ba5f NS |
410 | bmap_cmd.name = _("bmap"); |
411 | bmap_cmd.cfunc = bmap_f; | |
412 | bmap_cmd.argmin = 0; | |
413 | bmap_cmd.argmax = -1; | |
48c46ee3 | 414 | bmap_cmd.flags = CMD_NOMAP_OK; |
e246ba5f NS |
415 | bmap_cmd.args = _("[-adlpv] [-n nx]"); |
416 | bmap_cmd.oneline = _("print block mapping for an XFS file"); | |
417 | bmap_cmd.help = bmap_help; | |
2bd0ea18 | 418 | |
e246ba5f | 419 | add_command(&bmap_cmd); |
2bd0ea18 | 420 | } |