|  | // SPDX-License-Identifier: GPL-2.0+ | 
|  | /* | 
|  | * Copyright (C) 2018 Oracle.  All Rights Reserved. | 
|  | * Author: Darrick J. Wong <darrick.wong@oracle.com> | 
|  | */ | 
|  | #include "xfs.h" | 
|  | #include <stdint.h> | 
|  | #include <stdlib.h> | 
|  | #include <unistd.h> | 
|  | #include <string.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/statvfs.h> | 
|  | #include "libfrog/paths.h" | 
|  | #include "xfs_scrub.h" | 
|  | #include "common.h" | 
|  | #include "filemap.h" | 
|  |  | 
|  | /* | 
|  | * These routines provide a simple interface to query the block | 
|  | * mappings of the fork of a given inode via GETBMAPX and call a | 
|  | * function to iterate each mapping result. | 
|  | */ | 
|  |  | 
|  | #define BMAP_NR		2048 | 
|  |  | 
|  | /* | 
|  | * Iterate all the extent block mappings between the key and fork end. | 
|  | * Returns 0 or a positive error number. | 
|  | */ | 
|  | int | 
|  | scrub_iterate_filemaps( | 
|  | struct scrub_ctx	*ctx, | 
|  | int			fd, | 
|  | int			whichfork, | 
|  | struct file_bmap	*key, | 
|  | scrub_bmap_iter_fn	fn, | 
|  | void			*arg) | 
|  | { | 
|  | struct fsxattr		fsx; | 
|  | struct getbmapx		*map; | 
|  | struct getbmapx		*p; | 
|  | struct file_bmap	bmap; | 
|  | xfs_off_t		new_off; | 
|  | int			getxattr_type; | 
|  | int			i; | 
|  | int			ret; | 
|  |  | 
|  | map = calloc(BMAP_NR, sizeof(struct getbmapx)); | 
|  | if (!map) | 
|  | return errno; | 
|  |  | 
|  | map->bmv_offset = BTOBB(key->bm_offset); | 
|  | map->bmv_block = BTOBB(key->bm_physical); | 
|  | if (key->bm_length == 0) | 
|  | map->bmv_length = ULLONG_MAX; | 
|  | else | 
|  | map->bmv_length = BTOBB(key->bm_length); | 
|  | map->bmv_iflags = BMV_IF_PREALLOC | BMV_IF_NO_HOLES; | 
|  | switch (whichfork) { | 
|  | case XFS_ATTR_FORK: | 
|  | getxattr_type = XFS_IOC_FSGETXATTRA; | 
|  | map->bmv_iflags |= BMV_IF_ATTRFORK; | 
|  | break; | 
|  | case XFS_COW_FORK: | 
|  | map->bmv_iflags |= BMV_IF_COWFORK; | 
|  | getxattr_type = FS_IOC_FSGETXATTR; | 
|  | break; | 
|  | case XFS_DATA_FORK: | 
|  | getxattr_type = FS_IOC_FSGETXATTR; | 
|  | break; | 
|  | default: | 
|  | abort(); | 
|  | } | 
|  |  | 
|  | ret = ioctl(fd, getxattr_type, &fsx); | 
|  | if (ret < 0) { | 
|  | ret = errno; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (fsx.fsx_nextents == 0) | 
|  | goto out; | 
|  |  | 
|  | map->bmv_count = min(fsx.fsx_nextents + 1, BMAP_NR); | 
|  |  | 
|  | while ((ret = ioctl(fd, XFS_IOC_GETBMAPX, map)) == 0) { | 
|  | for (i = 0, p = &map[i + 1]; i < map->bmv_entries; i++, p++) { | 
|  | bmap.bm_offset = BBTOB(p->bmv_offset); | 
|  | bmap.bm_physical = BBTOB(p->bmv_block); | 
|  | bmap.bm_length = BBTOB(p->bmv_length); | 
|  | bmap.bm_flags = p->bmv_oflags; | 
|  | ret = fn(ctx, fd, whichfork, &fsx, &bmap, arg); | 
|  | if (ret) | 
|  | goto out; | 
|  | if (scrub_excessive_errors(ctx)) | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (map->bmv_entries == 0) | 
|  | break; | 
|  | p = map + map->bmv_entries; | 
|  | if (p->bmv_oflags & BMV_OF_LAST) | 
|  | break; | 
|  |  | 
|  | new_off = p->bmv_offset + p->bmv_length; | 
|  | map->bmv_length -= new_off - map->bmv_offset; | 
|  | map->bmv_offset = new_off; | 
|  | } | 
|  | if (ret < 0) | 
|  | ret = errno; | 
|  |  | 
|  | /* | 
|  | * Pre-reflink filesystems don't know about CoW forks, so don't | 
|  | * be too surprised if it fails. | 
|  | */ | 
|  | if (whichfork == XFS_COW_FORK && ret == EINVAL) | 
|  | ret = 0; | 
|  | out: | 
|  | free(map); | 
|  | return ret; | 
|  | } |