blob: 8e968c9f2455d542500da7117aca514297df13c7 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2003-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include <linux/falloc.h>
#include "command.h"
#include "input.h"
#include "init.h"
#include "io.h"
#ifndef FALLOC_FL_PUNCH_HOLE
#define FALLOC_FL_PUNCH_HOLE 0x02
#endif
#ifndef FALLOC_FL_COLLAPSE_RANGE
#define FALLOC_FL_COLLAPSE_RANGE 0x08
#endif
#ifndef FALLOC_FL_ZERO_RANGE
#define FALLOC_FL_ZERO_RANGE 0x10
#endif
#ifndef FALLOC_FL_INSERT_RANGE
#define FALLOC_FL_INSERT_RANGE 0x20
#endif
#ifndef FALLOC_FL_UNSHARE_RANGE
#define FALLOC_FL_UNSHARE_RANGE 0x40
#endif
static cmdinfo_t allocsp_cmd;
static cmdinfo_t freesp_cmd;
static cmdinfo_t resvsp_cmd;
static cmdinfo_t unresvsp_cmd;
static cmdinfo_t zero_cmd;
static cmdinfo_t falloc_cmd;
static cmdinfo_t fpunch_cmd;
static cmdinfo_t fcollapse_cmd;
static cmdinfo_t finsert_cmd;
static cmdinfo_t fzero_cmd;
static cmdinfo_t funshare_cmd;
static int
offset_length(
char *offset,
char *length,
xfs_flock64_t *segment)
{
size_t blocksize, sectsize;
init_cvtnum(&blocksize, &sectsize);
memset(segment, 0, sizeof(*segment));
segment->l_whence = SEEK_SET;
segment->l_start = cvtnum(blocksize, sectsize, offset);
if (segment->l_start < 0) {
printf(_("non-numeric offset argument -- %s\n"), offset);
return 0;
}
segment->l_len = cvtnum(blocksize, sectsize, length);
if (segment->l_len < 0) {
printf(_("non-numeric length argument -- %s\n"), length);
return 0;
}
return 1;
}
/*
* These ioctls were withdrawn in Linux 5.17, but we'll keep them around for
* a few releases.
*/
#ifndef XFS_IOC_ALLOCSP64
# define XFS_IOC_ALLOCSP64 _IOW ('X', 36, struct xfs_flock64)
#endif
#ifndef XFS_IOC_FREESP64
# define XFS_IOC_FREESP64 _IOW ('X', 37, struct xfs_flock64)
#endif
static int
allocsp_f(
int argc,
char **argv)
{
xfs_flock64_t segment;
if (!offset_length(argv[1], argv[2], &segment)) {
exitcode = 1;
return 0;
}
if (xfsctl(file->name, file->fd, XFS_IOC_ALLOCSP64, &segment) < 0) {
perror("XFS_IOC_ALLOCSP64");
exitcode = 1;
return 0;
}
return 0;
}
static int
freesp_f(
int argc,
char **argv)
{
xfs_flock64_t segment;
if (!offset_length(argv[1], argv[2], &segment)) {
exitcode = 1;
return 0;
}
if (xfsctl(file->name, file->fd, XFS_IOC_FREESP64, &segment) < 0) {
perror("XFS_IOC_FREESP64");
exitcode = 1;
return 0;
}
return 0;
}
static int
resvsp_f(
int argc,
char **argv)
{
xfs_flock64_t segment;
if (!offset_length(argv[1], argv[2], &segment)) {
exitcode = 1;
return 0;
}
if (xfsctl(file->name, file->fd, XFS_IOC_RESVSP64, &segment) < 0) {
perror("XFS_IOC_RESVSP64");
exitcode = 1;
return 0;
}
return 0;
}
static int
unresvsp_f(
int argc,
char **argv)
{
xfs_flock64_t segment;
if (!offset_length(argv[1], argv[2], &segment)) {
exitcode = 1;
return 0;
}
if (xfsctl(file->name, file->fd, XFS_IOC_UNRESVSP64, &segment) < 0) {
perror("XFS_IOC_UNRESVSP64");
exitcode = 1;
return 0;
}
return 0;
}
static int
zero_f(
int argc,
char **argv)
{
xfs_flock64_t segment;
if (!offset_length(argv[1], argv[2], &segment)) {
exitcode = 1;
return 0;
}
if (xfsctl(file->name, file->fd, XFS_IOC_ZERO_RANGE, &segment) < 0) {
perror("XFS_IOC_ZERO_RANGE");
exitcode = 1;
return 0;
}
return 0;
}
static void
falloc_help(void)
{
printf(_(
"\n"
" modifies space associated with part of a file via fallocate"
"\n"
" Example:\n"
" 'falloc 0 1m' - fills all holes within the first megabyte\n"
"\n"
" falloc uses the fallocate system call to alter space allocations in the\n"
" open file. The following operations are supported:\n"
" All the file offsets are in units of bytes.\n"
" -c -- collapses the given range.\n"
" -i -- inserts a hole into the given range of the file.\n"
" -k -- do not change file size.\n"
" -p -- unmap the given range from the file.\n"
" -u -- unshare shared extents in the given range.\n"
"\n"));
}
static int
fallocate_f(
int argc,
char **argv)
{
xfs_flock64_t segment;
int mode = 0;
int c;
while ((c = getopt(argc, argv, "cikpu")) != EOF) {
switch (c) {
case 'c':
mode = FALLOC_FL_COLLAPSE_RANGE;
break;
case 'i':
mode = FALLOC_FL_INSERT_RANGE;
break;
case 'k':
mode = FALLOC_FL_KEEP_SIZE;
break;
case 'p':
mode = FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE;
break;
case 'u':
mode = FALLOC_FL_UNSHARE_RANGE;
break;
default:
exitcode = 1;
command_usage(&falloc_cmd);
}
}
if (optind != argc - 2) {
exitcode = 1;
return command_usage(&falloc_cmd);
}
if (!offset_length(argv[optind], argv[optind+1], &segment)) {
exitcode = 1;
return 0;
}
if (fallocate(file->fd, mode,
segment.l_start, segment.l_len)) {
perror("fallocate");
exitcode = 1;
return 0;
}
return 0;
}
static int
fpunch_f(
int argc,
char **argv)
{
xfs_flock64_t segment;
int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
if (!offset_length(argv[1], argv[2], &segment)) {
exitcode = 1;
return 0;
}
if (fallocate(file->fd, mode,
segment.l_start, segment.l_len)) {
perror("fallocate");
exitcode = 1;
return 0;
}
return 0;
}
static int
fcollapse_f(
int argc,
char **argv)
{
xfs_flock64_t segment;
int mode = FALLOC_FL_COLLAPSE_RANGE;
if (!offset_length(argv[1], argv[2], &segment)) {
exitcode = 1;
return 0;
}
if (fallocate(file->fd, mode,
segment.l_start, segment.l_len)) {
perror("fallocate");
exitcode = 1;
return 0;
}
return 0;
}
static int
finsert_f(
int argc,
char **argv)
{
xfs_flock64_t segment;
int mode = FALLOC_FL_INSERT_RANGE;
if (!offset_length(argv[1], argv[2], &segment)) {
exitcode = 1;
return 0;
}
if (fallocate(file->fd, mode,
segment.l_start, segment.l_len)) {
perror("fallocate");
exitcode = 1;
return 0;
}
return 0;
}
static int
fzero_f(
int argc,
char **argv)
{
xfs_flock64_t segment;
int mode = FALLOC_FL_ZERO_RANGE;
int c;
while ((c = getopt(argc, argv, "k")) != EOF) {
switch (c) {
case 'k':
mode |= FALLOC_FL_KEEP_SIZE;
break;
default:
command_usage(&fzero_cmd);
}
}
if (optind != argc - 2)
return command_usage(&fzero_cmd);
if (!offset_length(argv[optind], argv[optind + 1], &segment))
return 0;
if (fallocate(file->fd, mode, segment.l_start, segment.l_len)) {
perror("fallocate");
return 0;
}
return 0;
}
static int
funshare_f(
int argc,
char **argv)
{
xfs_flock64_t segment;
int c;
int mode = FALLOC_FL_UNSHARE_RANGE;
while ((c = getopt(argc, argv, "")) != EOF) {
switch (c) {
default:
command_usage(&funshare_cmd);
}
}
if (optind != argc - 2)
return command_usage(&funshare_cmd);
if (!offset_length(argv[optind], argv[optind + 1], &segment)) {
exitcode = 1;
return 0;
}
if (fallocate(file->fd, mode, segment.l_start, segment.l_len)) {
perror("fallocate");
exitcode = 1;
return 0;
}
return 0;
}
void
prealloc_init(void)
{
allocsp_cmd.name = "allocsp";
allocsp_cmd.cfunc = allocsp_f;
allocsp_cmd.argmin = 2;
allocsp_cmd.argmax = 2;
allocsp_cmd.flags = CMD_NOMAP_OK;
allocsp_cmd.args = _("off len");
allocsp_cmd.oneline = _("allocates zeroed space for part of a file");
freesp_cmd.name = "freesp";
freesp_cmd.cfunc = freesp_f;
freesp_cmd.argmin = 2;
freesp_cmd.argmax = 2;
freesp_cmd.flags = CMD_NOMAP_OK;
freesp_cmd.args = _("off len");
freesp_cmd.oneline = _("frees space associated with part of a file");
resvsp_cmd.name = "resvsp";
resvsp_cmd.cfunc = resvsp_f;
resvsp_cmd.argmin = 2;
resvsp_cmd.argmax = 2;
resvsp_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
resvsp_cmd.args = _("off len");
resvsp_cmd.oneline =
_("reserves space associated with part of a file");
unresvsp_cmd.name = "unresvsp";
unresvsp_cmd.cfunc = unresvsp_f;
unresvsp_cmd.argmin = 2;
unresvsp_cmd.argmax = 2;
unresvsp_cmd.args = _("off len");
unresvsp_cmd.flags = CMD_NOMAP_OK;
unresvsp_cmd.oneline =
_("frees reserved space associated with part of a file");
zero_cmd.name = "zero";
zero_cmd.cfunc = zero_f;
zero_cmd.argmin = 2;
zero_cmd.argmax = 2;
zero_cmd.flags = CMD_NOMAP_OK;
zero_cmd.args = _("off len");
zero_cmd.oneline =
_("Converts the given range of a file to allocated zeros");
add_command(&allocsp_cmd);
add_command(&freesp_cmd);
add_command(&resvsp_cmd);
add_command(&unresvsp_cmd);
add_command(&zero_cmd);
falloc_cmd.name = "falloc";
falloc_cmd.cfunc = fallocate_f;
falloc_cmd.argmin = 2;
falloc_cmd.argmax = -1;
falloc_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
falloc_cmd.args = _("[-c] [-k] [-p] [-u] off len");
falloc_cmd.oneline =
_("allocates space associated with part of a file via fallocate");
falloc_cmd.help = falloc_help;
add_command(&falloc_cmd);
fpunch_cmd.name = "fpunch";
fpunch_cmd.cfunc = fpunch_f;
fpunch_cmd.argmin = 2;
fpunch_cmd.argmax = 2;
fpunch_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
fpunch_cmd.args = _("off len");
fpunch_cmd.oneline =
_("de-allocates space associated with part of a file via fallocate");
add_command(&fpunch_cmd);
fcollapse_cmd.name = "fcollapse";
fcollapse_cmd.cfunc = fcollapse_f;
fcollapse_cmd.argmin = 2;
fcollapse_cmd.argmax = 2;
fcollapse_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
fcollapse_cmd.args = _("off len");
fcollapse_cmd.oneline =
_("de-allocates space and eliminates the hole by shifting extents");
add_command(&fcollapse_cmd);
finsert_cmd.name = "finsert";
finsert_cmd.cfunc = finsert_f;
finsert_cmd.argmin = 2;
finsert_cmd.argmax = 2;
finsert_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
finsert_cmd.args = _("off len");
finsert_cmd.oneline =
_("creates new space for writing within file by shifting extents");
add_command(&finsert_cmd);
fzero_cmd.name = "fzero";
fzero_cmd.cfunc = fzero_f;
fzero_cmd.argmin = 2;
fzero_cmd.argmax = 3;
fzero_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
fzero_cmd.args = _("[-k] off len");
fzero_cmd.oneline =
_("zeroes space and eliminates holes by preallocating");
add_command(&fzero_cmd);
funshare_cmd.name = "funshare";
funshare_cmd.cfunc = funshare_f;
funshare_cmd.argmin = 2;
funshare_cmd.argmax = 2;
funshare_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
funshare_cmd.args = _("off len");
funshare_cmd.oneline =
_("unshares shared blocks within the range");
add_command(&funshare_cmd);
}