alloc: upgrade to fallocate
[xfstests-dev.git] / src / alloc.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2000-2003 Silicon Graphics, Inc.
4  * All Rights Reserved.
5  */
6  
7 #include "global.h"
8
9 /*
10  * Block I/O parameterization.  A basic block (BB) is the lowest size of
11  * filesystem allocation, and must equal 512.  Length units given to bio
12  * routines are in BB's.
13  */
14
15 /* Assume that if we have BTOBB, then we have the rest */
16 #ifndef BTOBB
17 #define BBSHIFT         9
18 #define BBSIZE          (1<<BBSHIFT)
19 #define BBMASK          (BBSIZE-1)
20 #define BTOBB(bytes)    (((__u64)(bytes) + BBSIZE - 1) >> BBSHIFT)
21 #define BTOBBT(bytes)   ((__u64)(bytes) >> BBSHIFT)
22 #define BBTOB(bbs)      ((bbs) << BBSHIFT)
23 #define OFFTOBBT(bytes) ((__u64)(bytes) >> BBSHIFT)
24
25 #define SEEKLIMIT32     0x7fffffff
26 #define BBSEEKLIMIT32   BTOBBT(SEEKLIMIT32)
27 #define SEEKLIMIT       0x7fffffffffffffffLL
28 #define BBSEEKLIMIT     OFFTOBBT(SEEKLIMIT)
29 #endif
30
31 #ifndef OFFTOBB
32 #define OFFTOBB(bytes)  (((__u64)(bytes) + BBSIZE - 1) >> BBSHIFT)
33 #define BBTOOFF(bbs)    ((__u64)(bbs) << BBSHIFT)
34 #endif
35
36 #define FSBTOBB(f)      (OFFTOBBT(FSBTOOFF(f)))
37 #define BBTOFSB(b)      (OFFTOFSB(BBTOOFF(b)))
38 #define OFFTOFSB(o)     ((o) / blocksize)
39 #define FSBTOOFF(f)     ((f) * blocksize)
40
41 void usage(void)
42 {
43     printf("usage: alloc [-b blocksize] [-d dir] [-f file] [-n] [-r] [-t]\n"
44            "flags:\n"
45            "    -n - non-interractive mode\n"
46            "    -r - real time file\n"
47            "    -t - truncate on open\n"
48            "\n"  
49            "commands:\n"  
50            "    r   [offset] [length] - reserve\n"
51            "    u   [offset] [length] - unreserve\n"
52            "    a   [offset] [length] - alloc      *** identical to free\n"
53            "    f   [offset] [length] - free       *** identical to alloc\n" 
54            "    m/p [offset] [length] - print map\n"
55            "    s                     - sync file\n"
56            "    t   [offset]          - truncate\n"
57            "    q                     - quit\n"
58            "    h/?                   - this help\n");
59            
60 }
61
62 int             fd = -1;
63 int             blocksize;
64 char            *filename;
65
66 /* params are in bytes */
67 void map(off64_t off, off64_t len)
68 {
69     struct getbmap      bm[2];
70     
71     bzero(bm, sizeof(bm));
72
73     bm[0].bmv_count = 2;
74     bm[0].bmv_offset = OFFTOBB(off);
75     if (len==(off64_t)-1) { /* unsigned... */
76         bm[0].bmv_length = -1;
77         printf("    MAP off=%lld, len=%lld [%lld-]\n", 
78                 (long long)off, (long long)len,
79                 (long long)BBTOFSB(bm[0].bmv_offset));
80     } else {
81         bm[0].bmv_length = OFFTOBB(len);
82         printf("    MAP off=%lld, len=%lld [%lld,%lld]\n", 
83                 (long long)off, (long long)len,
84                 (long long)BBTOFSB(bm[0].bmv_offset),
85                 (long long)BBTOFSB(bm[0].bmv_length));
86     }
87     
88     printf("        [ofs,count]: start..end\n");
89     for (;;) {
90 #ifdef XFS_IOC_GETBMAP
91             if (xfsctl(filename, fd, XFS_IOC_GETBMAP, bm) < 0) {
92 #else
93 #ifdef F_GETBMAP
94             if (fcntl(fd, F_GETBMAP, bm) < 0) {
95 #else
96 bozo!
97 #endif
98 #endif
99                     perror("getbmap");
100                     break;
101             }
102
103             if (bm[0].bmv_entries == 0)
104                     break;
105
106             printf("        [%lld,%lld]: ",
107                     (long long)BBTOFSB(bm[1].bmv_offset),
108                     (long long)BBTOFSB(bm[1].bmv_length));
109
110             if (bm[1].bmv_block == -1)
111                     printf("hole");
112             else
113                     printf("%lld..%lld",
114                             (long long)BBTOFSB(bm[1].bmv_block),
115                             (long long)BBTOFSB(bm[1].bmv_block +
116                                     bm[1].bmv_length - 1));
117             printf("\n");
118     }
119 }
120
121 #ifdef HAVE_FALLOCATE
122 # define USE_LINUX_PREALLOCATE
123 enum linux_opno {
124         FREESP = 0,
125         ALLOCSP,
126         UNRESVSP,
127         RESVSP,
128 };
129
130 /* emulate the irix preallocation functions with linux vfs calls */
131 static int
132 linux_preallocate(
133         int                     fd,
134         enum linux_opno         opno,
135         const struct flock64    *f)
136 {
137         struct stat             sbuf;
138         int                     ret;
139
140         assert(f->l_whence == SEEK_SET);
141
142         switch (opno) {
143         case FREESP:
144                 return ftruncate(fd, f->l_start);
145         case ALLOCSP:
146                 ret = fstat(fd, &sbuf);
147                 if (ret)
148                         return ret;
149
150                 return fallocate(fd, 0, sbuf.st_size,
151                                 f->l_start - sbuf.st_size);
152         case UNRESVSP:
153                 return fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
154                                 f->l_start, f->l_len);
155         case RESVSP:
156                 return fallocate(fd, FALLOC_FL_KEEP_SIZE, f->l_start, f->l_len);
157         }
158
159         /* should never get here */
160         errno = EINVAL;
161         return -1;
162 }
163 #endif
164
165 int
166 main(int argc, char **argv)
167 {
168         int             c;
169         char            *dirname = NULL;
170         int             done = 0;
171         int             status = 0;
172         struct flock64  f;
173         off64_t         len;
174         char            line[1024];
175         off64_t         off;
176         int             oflags;
177         static char     *opnames[] = { "freesp",
178                                        "allocsp",
179                                        "unresvsp",
180                                        "resvsp" };
181         int             opno;
182
183 #if defined(HAVE_FALLOCATE)
184         /* see static function above */
185 #elif defined(XFS_IOC_FREESP64)
186 #define USE_XFSCTL
187         /* Assume that if we have FREESP64 then we have the rest */
188         static int      optab[] = { XFS_IOC_FREESP64,
189                                     XFS_IOC_ALLOCSP64,
190                                     XFS_IOC_UNRESVSP64,
191                                     XFS_IOC_RESVSP64 };
192 #elif defined(F_FREESP64)
193 #define USE_FCNTL
194         static int      optab[] = { F_FREESP64,
195                                     F_ALLOCSP64,
196                                     F_UNRESVSP64,
197                                     F_RESVSP64 };
198 #else
199 # error Dont know how to preallocate space!
200 #endif
201         int             rflag = 0;
202         struct statvfs64        svfs;
203         int             tflag = 0;
204         int             nflag = 0;
205         int             unlinkit = 0;
206         int64_t         v;
207
208         while ((c = getopt(argc, argv, "b:d:f:rtn")) != -1) {
209                 switch (c) {
210                 case 'b':
211                         blocksize = atoi(optarg);
212                         break;
213                 case 'd':
214                         if (filename) {
215                                 printf("can't specify both -d and -f\n");
216                                 exit(1);
217                         }
218                         dirname = optarg;
219                         break;
220                 case 'f':
221                         if (dirname) {
222                                 printf("can't specify both -d and -f\n");
223                                 exit(1);
224                         }
225                         filename = optarg;
226                         break;
227                 case 'r':
228                         rflag = 1;
229                         break;
230                 case 't':
231                         tflag = 1;
232                         break;
233                 case 'n':
234                         nflag++;
235                         break;
236                 default:
237                         printf("unknown option\n");
238                         usage();
239                         exit(1);
240                 }
241         }
242         if (!dirname && !filename)
243                 dirname = ".";
244         if (!filename) {
245                 static char     tmpfile[] = "allocXXXXXX";
246
247                 mkstemp(tmpfile);
248                 filename = malloc(strlen(tmpfile) + strlen(dirname) + 2);
249                 sprintf(filename, "%s/%s", dirname, tmpfile);
250                 unlinkit = 1;
251         }
252         oflags = O_RDWR | O_CREAT | (tflag ? O_TRUNC : 0);
253         fd = open(filename, oflags, 0666);
254         if (!nflag) {
255             printf("alloc:\n");
256             printf("    filename %s\n", filename);
257         }
258         if (fd < 0) {
259                 perror(filename);
260                 exit(1);
261         }
262         if (!blocksize) {
263                 if (fstatvfs64(fd, &svfs) < 0) {
264                         perror(filename);
265                         status = 1;
266                         goto done;
267                 }
268                 blocksize = (int)svfs.f_bsize;
269         }
270         if (blocksize<0) {
271                 fprintf(stderr,"illegal blocksize %d\n", blocksize);
272                 status = 1;
273                 goto done;
274         }
275         printf("    blocksize %d\n", blocksize);
276         if (rflag) {
277                 struct fsxattr a;
278
279 #ifdef XFS_IOC_FSGETXATTR
280                 if (xfsctl(filename, fd, XFS_IOC_FSGETXATTR, &a) < 0) {
281                         perror("XFS_IOC_FSGETXATTR");
282                         status = 1;
283                         goto done;
284                 }
285 #else
286 #ifdef F_FSGETXATTR
287                 if (fcntl(fd, F_FSGETXATTR, &a) < 0) {
288                         perror("F_FSGETXATTR");
289                         status = 1;
290                         goto done;
291                 }
292 #else
293 bozo!
294 #endif
295 #endif
296
297                 a.fsx_xflags |= XFS_XFLAG_REALTIME;
298
299 #ifdef XFS_IOC_FSSETXATTR
300                 if (xfsctl(filename, fd, XFS_IOC_FSSETXATTR, &a) < 0) {
301                         perror("XFS_IOC_FSSETXATTR");
302                         status = 1;
303                         goto done;
304                 }
305 #else
306 #ifdef F_FSSETXATTR
307                 if (fcntl(fd, F_FSSETXATTR, &a) < 0) {
308                         perror("F_FSSETXATTR");
309                         status = 1;
310                         goto done;
311                 }
312 #else
313 bozo!
314 #endif
315 #endif
316         }
317         while (!done) {
318                 char *p;
319                 
320                 if (!nflag) printf("alloc> ");
321                 fflush(stdout);
322                 if (!fgets(line, 1024, stdin)) break;
323                 
324                 p=line+strlen(line);
325                 if (p!=line&&p[-1]=='\n') p[-1]=0;
326                 
327                 opno = 0;
328                 switch (line[0]) {
329                 case 'r':
330                         opno++;
331                 case 'u':
332                         opno++;
333                 case 'a':
334                         opno++;
335                 case 'f':
336                         v = strtoll(&line[2], &p, 0);
337                         if (*p == 'b') {
338                                 off = FSBTOOFF(v);
339                                 p++;
340                         } else
341                                 off = v;
342                         f.l_whence = SEEK_SET;
343                         f.l_start = off;
344                         if (*p == '\0')
345                                 v = -1;
346                         else
347                                 v = strtoll(p, &p, 0);
348                         if (*p == 'b') {
349                                 len = FSBTOOFF(v);
350                                 p++;
351                         } else
352                                 len = v;
353                         
354                         printf("    CMD %s, off=%lld, len=%lld\n", 
355                                 opnames[opno], (long long)off, (long long)len);
356                         
357                         f.l_len = len;
358 #if defined(USE_LINUX_PREALLOCATE)
359                         c = linux_preallocate(fd, opno, &f);
360 #elif defined(USE_XFSCTL)
361                         c = xfsctl(filename, fd, optab[opno], &f);
362 #elif defined(USE_FCNTL)
363                         c = fcntl(fd, optab[opno], &f);
364 #else
365 # error Dont know how to preallocate space!
366 #endif
367                         if (c < 0) {
368                                 perror(opnames[opno]);
369                                 break;
370                         }
371                         
372                         map(off,len);                        
373                         break;
374                 case 'p':
375                 case 'm':
376                         p = &line[1];
377                         v = strtoll(p, &p, 0);
378                         if (*p == 'b') {
379                                 off = FSBTOOFF(v);
380                                 p++;
381                         } else
382                                 off = v;
383                         if (*p == '\0')
384                                 len = -1;
385                         else {
386                                 v = strtoll(p, &p, 0);
387                                 if (*p == 'b')
388                                         len = FSBTOOFF(v);
389                                 else
390                                         len = v;
391                         }
392                         map(off,len);
393                         break;
394                 case 't':
395                         p = &line[1];
396                         v = strtoll(p, &p, 0);
397                         if (*p == 'b')
398                                 off = FSBTOOFF(v);
399                         else
400                                 off = v;
401                         printf("    TRUNCATE off=%lld\n", (long long)off);
402                         if (ftruncate64(fd, off) < 0) {
403                                 perror("ftruncate");
404                                 break;
405                         }
406                         break;
407                 case 's':
408                         printf("    SYNC\n");
409                         fsync(fd);
410                         break;
411                 case 'q':
412                         printf("    QUIT\n");
413                         done = 1;
414                         break;
415                 case '?':
416                 case 'h':
417                         usage();
418                         break;
419                 default:
420                         printf("unknown command '%s'\n", line);
421                         break;
422                 }
423         }
424         if (!nflag) printf("\n");
425 done:
426         if (fd != -1)
427                 close(fd);
428         if (unlinkit)
429                 unlink(filename);
430         exit(status);
431         /* NOTREACHED */
432 }