| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (c) 2000-2003 Silicon Graphics, Inc. | |
| * All Rights Reserved. | |
| */ | |
| #include "global.h" | |
| /* | |
| * Block I/O parameterization. A basic block (BB) is the lowest size of | |
| * filesystem allocation, and must equal 512. Length units given to bio | |
| * routines are in BB's. | |
| */ | |
| /* Assume that if we have BTOBB, then we have the rest */ | |
| #ifndef BTOBB | |
| #define BBSHIFT 9 | |
| #define BBSIZE (1<<BBSHIFT) | |
| #define BBMASK (BBSIZE-1) | |
| #define BTOBB(bytes) (((__u64)(bytes) + BBSIZE - 1) >> BBSHIFT) | |
| #define BTOBBT(bytes) ((__u64)(bytes) >> BBSHIFT) | |
| #define BBTOB(bbs) ((bbs) << BBSHIFT) | |
| #define OFFTOBBT(bytes) ((__u64)(bytes) >> BBSHIFT) | |
| #define SEEKLIMIT32 0x7fffffff | |
| #define BBSEEKLIMIT32 BTOBBT(SEEKLIMIT32) | |
| #define SEEKLIMIT 0x7fffffffffffffffLL | |
| #define BBSEEKLIMIT OFFTOBBT(SEEKLIMIT) | |
| #endif | |
| #ifndef OFFTOBB | |
| #define OFFTOBB(bytes) (((__u64)(bytes) + BBSIZE - 1) >> BBSHIFT) | |
| #define BBTOOFF(bbs) ((__u64)(bbs) << BBSHIFT) | |
| #endif | |
| #define FSBTOBB(f) (OFFTOBBT(FSBTOOFF(f))) | |
| #define BBTOFSB(b) (OFFTOFSB(BBTOOFF(b))) | |
| #define OFFTOFSB(o) ((o) / blocksize) | |
| #define FSBTOOFF(f) ((f) * blocksize) | |
| void usage(void) | |
| { | |
| printf("usage: alloc [-b blocksize] [-d dir] [-f file] [-n] [-r] [-t]\n" | |
| "flags:\n" | |
| " -n - non-interractive mode\n" | |
| " -r - real time file\n" | |
| " -t - truncate on open\n" | |
| "\n" | |
| "commands:\n" | |
| " r [offset] [length] - reserve\n" | |
| " u [offset] [length] - unreserve\n" | |
| " a [offset] [length] - alloc *** identical to free\n" | |
| " f [offset] [length] - free *** identical to alloc\n" | |
| " m/p [offset] [length] - print map\n" | |
| " s - sync file\n" | |
| " t [offset] - truncate\n" | |
| " q - quit\n" | |
| " h/? - this help\n"); | |
| } | |
| int fd = -1; | |
| int blocksize; | |
| char *filename; | |
| /* params are in bytes */ | |
| void map(off64_t off, off64_t len) | |
| { | |
| struct getbmap bm[2]; | |
| bzero(bm, sizeof(bm)); | |
| bm[0].bmv_count = 2; | |
| bm[0].bmv_offset = OFFTOBB(off); | |
| if (len==(off64_t)-1) { /* unsigned... */ | |
| bm[0].bmv_length = -1; | |
| printf(" MAP off=%lld, len=%lld [%lld-]\n", | |
| (long long)off, (long long)len, | |
| (long long)BBTOFSB(bm[0].bmv_offset)); | |
| } else { | |
| bm[0].bmv_length = OFFTOBB(len); | |
| printf(" MAP off=%lld, len=%lld [%lld,%lld]\n", | |
| (long long)off, (long long)len, | |
| (long long)BBTOFSB(bm[0].bmv_offset), | |
| (long long)BBTOFSB(bm[0].bmv_length)); | |
| } | |
| printf(" [ofs,count]: start..end\n"); | |
| for (;;) { | |
| #ifdef XFS_IOC_GETBMAP | |
| if (xfsctl(filename, fd, XFS_IOC_GETBMAP, bm) < 0) { | |
| #else | |
| #ifdef F_GETBMAP | |
| if (fcntl(fd, F_GETBMAP, bm) < 0) { | |
| #else | |
| bozo! | |
| #endif | |
| #endif | |
| perror("getbmap"); | |
| break; | |
| } | |
| if (bm[0].bmv_entries == 0) | |
| break; | |
| printf(" [%lld,%lld]: ", | |
| (long long)BBTOFSB(bm[1].bmv_offset), | |
| (long long)BBTOFSB(bm[1].bmv_length)); | |
| if (bm[1].bmv_block == -1) | |
| printf("hole"); | |
| else | |
| printf("%lld..%lld", | |
| (long long)BBTOFSB(bm[1].bmv_block), | |
| (long long)BBTOFSB(bm[1].bmv_block + | |
| bm[1].bmv_length - 1)); | |
| printf("\n"); | |
| } | |
| } | |
| #ifdef HAVE_FALLOCATE | |
| # define USE_LINUX_PREALLOCATE | |
| enum linux_opno { | |
| FREESP = 0, | |
| ALLOCSP, | |
| UNRESVSP, | |
| RESVSP, | |
| }; | |
| /* emulate the irix preallocation functions with linux vfs calls */ | |
| static int | |
| linux_preallocate( | |
| int fd, | |
| enum linux_opno opno, | |
| const struct flock64 *f) | |
| { | |
| struct stat sbuf; | |
| int ret; | |
| assert(f->l_whence == SEEK_SET); | |
| switch (opno) { | |
| case FREESP: | |
| return ftruncate(fd, f->l_start); | |
| case ALLOCSP: | |
| ret = fstat(fd, &sbuf); | |
| if (ret) | |
| return ret; | |
| return fallocate(fd, 0, sbuf.st_size, | |
| f->l_start - sbuf.st_size); | |
| case UNRESVSP: | |
| return fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, | |
| f->l_start, f->l_len); | |
| case RESVSP: | |
| return fallocate(fd, FALLOC_FL_KEEP_SIZE, f->l_start, f->l_len); | |
| } | |
| /* should never get here */ | |
| errno = EINVAL; | |
| return -1; | |
| } | |
| #endif | |
| int | |
| main(int argc, char **argv) | |
| { | |
| int c; | |
| char *dirname = NULL; | |
| int done = 0; | |
| int status = 0; | |
| struct flock64 f; | |
| off64_t len; | |
| char line[1024]; | |
| off64_t off; | |
| int oflags; | |
| static char *opnames[] = { "freesp", | |
| "allocsp", | |
| "unresvsp", | |
| "resvsp" }; | |
| int opno; | |
| #if defined(HAVE_FALLOCATE) | |
| /* see static function above */ | |
| #elif defined(XFS_IOC_FREESP64) | |
| #define USE_XFSCTL | |
| /* Assume that if we have FREESP64 then we have the rest */ | |
| static int optab[] = { XFS_IOC_FREESP64, | |
| XFS_IOC_ALLOCSP64, | |
| XFS_IOC_UNRESVSP64, | |
| XFS_IOC_RESVSP64 }; | |
| #elif defined(F_FREESP64) | |
| #define USE_FCNTL | |
| static int optab[] = { F_FREESP64, | |
| F_ALLOCSP64, | |
| F_UNRESVSP64, | |
| F_RESVSP64 }; | |
| #else | |
| # error Dont know how to preallocate space! | |
| #endif | |
| int rflag = 0; | |
| struct statvfs64 svfs; | |
| int tflag = 0; | |
| int nflag = 0; | |
| int unlinkit = 0; | |
| int64_t v; | |
| while ((c = getopt(argc, argv, "b:d:f:rtn")) != -1) { | |
| switch (c) { | |
| case 'b': | |
| blocksize = atoi(optarg); | |
| break; | |
| case 'd': | |
| if (filename) { | |
| printf("can't specify both -d and -f\n"); | |
| exit(1); | |
| } | |
| dirname = optarg; | |
| break; | |
| case 'f': | |
| if (dirname) { | |
| printf("can't specify both -d and -f\n"); | |
| exit(1); | |
| } | |
| filename = optarg; | |
| break; | |
| case 'r': | |
| rflag = 1; | |
| break; | |
| case 't': | |
| tflag = 1; | |
| break; | |
| case 'n': | |
| nflag++; | |
| break; | |
| default: | |
| printf("unknown option\n"); | |
| usage(); | |
| exit(1); | |
| } | |
| } | |
| if (!dirname && !filename) | |
| dirname = "."; | |
| if (!filename) { | |
| static char tmpfile[] = "allocXXXXXX"; | |
| mkstemp(tmpfile); | |
| filename = malloc(strlen(tmpfile) + strlen(dirname) + 2); | |
| sprintf(filename, "%s/%s", dirname, tmpfile); | |
| unlinkit = 1; | |
| } | |
| oflags = O_RDWR | O_CREAT | (tflag ? O_TRUNC : 0); | |
| fd = open(filename, oflags, 0666); | |
| if (!nflag) { | |
| printf("alloc:\n"); | |
| printf(" filename %s\n", filename); | |
| } | |
| if (fd < 0) { | |
| perror(filename); | |
| exit(1); | |
| } | |
| if (!blocksize) { | |
| if (fstatvfs64(fd, &svfs) < 0) { | |
| perror(filename); | |
| status = 1; | |
| goto done; | |
| } | |
| blocksize = (int)svfs.f_bsize; | |
| } | |
| if (blocksize<0) { | |
| fprintf(stderr,"illegal blocksize %d\n", blocksize); | |
| status = 1; | |
| goto done; | |
| } | |
| printf(" blocksize %d\n", blocksize); | |
| if (rflag) { | |
| struct fsxattr a; | |
| #ifdef XFS_IOC_FSGETXATTR | |
| if (xfsctl(filename, fd, XFS_IOC_FSGETXATTR, &a) < 0) { | |
| perror("XFS_IOC_FSGETXATTR"); | |
| status = 1; | |
| goto done; | |
| } | |
| #else | |
| #ifdef F_FSGETXATTR | |
| if (fcntl(fd, F_FSGETXATTR, &a) < 0) { | |
| perror("F_FSGETXATTR"); | |
| status = 1; | |
| goto done; | |
| } | |
| #else | |
| bozo! | |
| #endif | |
| #endif | |
| a.fsx_xflags |= XFS_XFLAG_REALTIME; | |
| #ifdef XFS_IOC_FSSETXATTR | |
| if (xfsctl(filename, fd, XFS_IOC_FSSETXATTR, &a) < 0) { | |
| perror("XFS_IOC_FSSETXATTR"); | |
| status = 1; | |
| goto done; | |
| } | |
| #else | |
| #ifdef F_FSSETXATTR | |
| if (fcntl(fd, F_FSSETXATTR, &a) < 0) { | |
| perror("F_FSSETXATTR"); | |
| status = 1; | |
| goto done; | |
| } | |
| #else | |
| bozo! | |
| #endif | |
| #endif | |
| } | |
| while (!done) { | |
| char *p; | |
| if (!nflag) printf("alloc> "); | |
| fflush(stdout); | |
| if (!fgets(line, 1024, stdin)) break; | |
| p=line+strlen(line); | |
| if (p!=line&&p[-1]=='\n') p[-1]=0; | |
| opno = 0; | |
| switch (line[0]) { | |
| case 'r': | |
| opno++; | |
| case 'u': | |
| opno++; | |
| case 'a': | |
| opno++; | |
| case 'f': | |
| v = strtoll(&line[2], &p, 0); | |
| if (*p == 'b') { | |
| off = FSBTOOFF(v); | |
| p++; | |
| } else | |
| off = v; | |
| f.l_whence = SEEK_SET; | |
| f.l_start = off; | |
| if (*p == '\0') | |
| v = -1; | |
| else | |
| v = strtoll(p, &p, 0); | |
| if (*p == 'b') { | |
| len = FSBTOOFF(v); | |
| p++; | |
| } else | |
| len = v; | |
| printf(" CMD %s, off=%lld, len=%lld\n", | |
| opnames[opno], (long long)off, (long long)len); | |
| f.l_len = len; | |
| #if defined(USE_LINUX_PREALLOCATE) | |
| c = linux_preallocate(fd, opno, &f); | |
| #elif defined(USE_XFSCTL) | |
| c = xfsctl(filename, fd, optab[opno], &f); | |
| #elif defined(USE_FCNTL) | |
| c = fcntl(fd, optab[opno], &f); | |
| #else | |
| # error Dont know how to preallocate space! | |
| #endif | |
| if (c < 0) { | |
| perror(opnames[opno]); | |
| break; | |
| } | |
| map(off,len); | |
| break; | |
| case 'p': | |
| case 'm': | |
| p = &line[1]; | |
| v = strtoll(p, &p, 0); | |
| if (*p == 'b') { | |
| off = FSBTOOFF(v); | |
| p++; | |
| } else | |
| off = v; | |
| if (*p == '\0') | |
| len = -1; | |
| else { | |
| v = strtoll(p, &p, 0); | |
| if (*p == 'b') | |
| len = FSBTOOFF(v); | |
| else | |
| len = v; | |
| } | |
| map(off,len); | |
| break; | |
| case 't': | |
| p = &line[1]; | |
| v = strtoll(p, &p, 0); | |
| if (*p == 'b') | |
| off = FSBTOOFF(v); | |
| else | |
| off = v; | |
| printf(" TRUNCATE off=%lld\n", (long long)off); | |
| if (ftruncate64(fd, off) < 0) { | |
| perror("ftruncate"); | |
| break; | |
| } | |
| break; | |
| case 's': | |
| printf(" SYNC\n"); | |
| fsync(fd); | |
| break; | |
| case 'q': | |
| printf(" QUIT\n"); | |
| done = 1; | |
| break; | |
| case '?': | |
| case 'h': | |
| usage(); | |
| break; | |
| default: | |
| printf("unknown command '%s'\n", line); | |
| break; | |
| } | |
| } | |
| if (!nflag) printf("\n"); | |
| done: | |
| if (fd != -1) | |
| close(fd); | |
| if (unlinkit) | |
| unlink(filename); | |
| exit(status); | |
| /* NOTREACHED */ | |
| } |