| // 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, §size); |
| 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); |
| } |