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