blob: bad2e9e1bb149a16cf3812b0c0b197a8253b3e67 [file] [log] [blame]
// 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_NO_DMAPI_READ | 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 (xfs_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;
}