]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/filemap.c
xfs_scrub: request fewer bmaps when we can
[thirdparty/xfsprogs-dev.git] / scrub / filemap.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
5 */
6 #include "xfs.h"
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/statvfs.h>
13 #include "libfrog/paths.h"
14 #include "xfs_scrub.h"
15 #include "common.h"
16 #include "filemap.h"
17
18 /*
19 * These routines provide a simple interface to query the block
20 * mappings of the fork of a given inode via GETBMAPX and call a
21 * function to iterate each mapping result.
22 */
23
24 #define BMAP_NR 2048
25
26 /* Iterate all the extent block mappings between the key and fork end. */
27 bool
28 xfs_iterate_filemaps(
29 struct scrub_ctx *ctx,
30 const char *descr,
31 int fd,
32 int whichfork,
33 struct xfs_bmap *key,
34 xfs_bmap_iter_fn fn,
35 void *arg)
36 {
37 struct fsxattr fsx;
38 struct getbmapx *map;
39 struct getbmapx *p;
40 struct xfs_bmap bmap;
41 char bmap_descr[DESCR_BUFSZ];
42 bool moveon = true;
43 xfs_off_t new_off;
44 int getxattr_type;
45 int i;
46 int error;
47
48 switch (whichfork) {
49 case XFS_ATTR_FORK:
50 snprintf(bmap_descr, DESCR_BUFSZ, _("%s attr"), descr);
51 break;
52 case XFS_COW_FORK:
53 snprintf(bmap_descr, DESCR_BUFSZ, _("%s CoW"), descr);
54 break;
55 case XFS_DATA_FORK:
56 snprintf(bmap_descr, DESCR_BUFSZ, _("%s data"), descr);
57 break;
58 default:
59 abort();
60 }
61
62 map = calloc(BMAP_NR, sizeof(struct getbmapx));
63 if (!map) {
64 str_errno(ctx, bmap_descr);
65 return false;
66 }
67
68 map->bmv_offset = BTOBB(key->bm_offset);
69 map->bmv_block = BTOBB(key->bm_physical);
70 if (key->bm_length == 0)
71 map->bmv_length = ULLONG_MAX;
72 else
73 map->bmv_length = BTOBB(key->bm_length);
74 map->bmv_iflags = BMV_IF_NO_DMAPI_READ | BMV_IF_PREALLOC |
75 BMV_IF_NO_HOLES;
76 switch (whichfork) {
77 case XFS_ATTR_FORK:
78 getxattr_type = XFS_IOC_FSGETXATTRA;
79 map->bmv_iflags |= BMV_IF_ATTRFORK;
80 break;
81 case XFS_COW_FORK:
82 map->bmv_iflags |= BMV_IF_COWFORK;
83 getxattr_type = FS_IOC_FSGETXATTR;
84 break;
85 case XFS_DATA_FORK:
86 getxattr_type = FS_IOC_FSGETXATTR;
87 break;
88 default:
89 abort();
90 }
91
92 error = ioctl(fd, getxattr_type, &fsx);
93 if (error < 0) {
94 str_errno(ctx, bmap_descr);
95 moveon = false;
96 goto out;
97 }
98
99 if (fsx.fsx_nextents == 0) {
100 moveon = true;
101 goto out;
102 }
103
104 map->bmv_count = min(fsx.fsx_nextents + 1, BMAP_NR);
105
106 while ((error = ioctl(fd, XFS_IOC_GETBMAPX, map)) == 0) {
107 for (i = 0, p = &map[i + 1]; i < map->bmv_entries; i++, p++) {
108 bmap.bm_offset = BBTOB(p->bmv_offset);
109 bmap.bm_physical = BBTOB(p->bmv_block);
110 bmap.bm_length = BBTOB(p->bmv_length);
111 bmap.bm_flags = p->bmv_oflags;
112 moveon = fn(ctx, bmap_descr, fd, whichfork, &fsx,
113 &bmap, arg);
114 if (!moveon)
115 goto out;
116 if (xfs_scrub_excessive_errors(ctx)) {
117 moveon = false;
118 goto out;
119 }
120 }
121
122 if (map->bmv_entries == 0)
123 break;
124 p = map + map->bmv_entries;
125 if (p->bmv_oflags & BMV_OF_LAST)
126 break;
127
128 new_off = p->bmv_offset + p->bmv_length;
129 map->bmv_length -= new_off - map->bmv_offset;
130 map->bmv_offset = new_off;
131 }
132
133 /*
134 * Pre-reflink filesystems don't know about CoW forks, so don't
135 * be too surprised if it fails.
136 */
137 if (whichfork == XFS_COW_FORK && error && errno == EINVAL)
138 error = 0;
139
140 if (error)
141 str_errno(ctx, bmap_descr);
142 out:
143 free(map);
144 return moveon;
145 }