xfs: test fallocate ops when rt extent size is and isn't a power of 2
[xfstests-dev.git] / dmapi / src / suite1 / cmd / get_allocinfo.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2000-2001 Silicon Graphics, Inc.
4  * All Rights Reserved.
5  */
6
7 #include <sys/types.h>
8 #include <sys/stat.h>
9
10 #include <ctype.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13
14 #include <lib/hsm.h>
15
16 #include <string.h>
17
18 /*---------------------------------------------------------------------------
19
20 Test program used to test the DMAPI function dm_get_allocinfo().  The
21 command line is:
22
23         get_allocinfo [-D] [-n nelem] [-o offp] [-s sid] pathname
24
25 where pathname is the name of a file, 'offp' is a byte offset from the
26 beginning of the file where you want to start dumping, and 'nelem' allows
27 you to specify how many extent structures to use in each dm_get_allocinfo
28 call.
29
30 The code checks the returned extents as much as possible for errors.
31 It detects bad ex_type values, verifies that there is always a trailing
32 hole at the end of the file, that the ex_offset of each extent matches the
33 ex_offset+ex_length of the previous extent, and that ex_offset+ex_length
34 is always an even multiple of 512.  It verifies that all ex_offset values
35 after the first fall on a 512-byte boundary.  It verifies that the '*offp'
36 value returned by dm_get_allocinfo() is 0 at the end of the file, and
37 equals ex_offset+ex_length of the last extent if not at the end of the file.
38 Any error is reported to stderr, and the program then terminates with a
39 non-zero exit status.
40
41 The program produces output similar to xfs_bmap in order to make comparison
42 easier.  Here is some sample output.
43
44 f1: offset 1
45         rc 0, nelemp 17
46         0: [0..127]: resv       [1..511]
47
48 Line 1 gives the name of the file and the byte offset within the file where
49 the dump started.  Line 2 appears once for each dm_get_allocinfo() call,
50 giving the return value (rc) and the number of extents which were returned.
51 Line 3 is repeated once for each extent.  The first field "0:" is the extent
52 number.  The second field "[0..127]:" give the starting and ending block for
53 the extent in 512-byte units.  The third field is either "resv" to indicate
54 allocated space or "hole" if the extent is a hole.  The fourth field
55 "[1..511]" only appears if the dump did not start with byte zero of the
56 first block.  In that case, the first number shows the actual byte offset
57 within the block (1 in this case).  The second number should always be
58 511 since we always dump to the end of a block.
59
60 Possible tests
61 --------------
62
63 Dump some holey files and compare the output of this program with xfs_bmap.
64
65 Produce a file with holes, and perform the following tests using just one
66 extent (-e 1).
67         Dump extents from beginning of the file.
68         Dump from byte 1 of the file.
69         Dump from the last byte of the first extent.
70         Dump from the first byte of the second extent.
71         Dump from the middle of the second extent.
72         Dump the first byte of the last extent.
73         Dump the last byte of the last extent.
74         Dump the first byte after the last extent.
75         Dump starting at an offset way past the end of the file.
76
77 Produce a fragmented file with many adjacent DM_EXTENT_RES extents.
78         Repeat the above tests.
79
80 Produce a GRIO file with holes.
81         Repeat the above tests.
82
83 Run the following shell script.
84
85 #!/bin/ksh
86
87 #       Dump the same holey file $max times, each time using one less extent
88 #       structure than the previous time.  The grep and diff code
89 #       checks to make sure that you get the same answer each time, no matter
90 #       how many extents you use.  If all is okay, The only messages you will
91 #       see are the "trial $num" messages.
92
93 max=20          # should be bigger than the number extents in the file
94
95 num=$max
96 while [ $num -gt 0 ]
97 do
98         echo "trial $num"
99         ./test_alloc -e $num f1 | grep '\[' > x.$num
100         if [ $num -lt $max ]
101         then
102                 diff x.$num x.$max
103         fi
104         num=`expr $num - 1`
105 done
106
107 ----------------------------------------------------------------------------*/
108
109 #ifndef linux
110 extern  char    *sys_errlist[];
111 #endif
112 extern  int     optind;
113 extern  char    *optarg;
114
115 static int print_alloc(dm_sessid_t sid, void* hanp, size_t hlen,
116         char *pathname, dm_off_t startoff, u_int nelem);
117
118 char    *Progname;
119 int     Dflag = 0;
120
121
122 static void
123 usage(void)
124 {
125         fprintf(stderr, "usage:\t%s [-D] [-n nelem] [-o off] [-s sid] "
126                 "pathname\n", Progname);
127         exit(1);
128 }
129
130
131 int
132 main(
133         int     argc, 
134         char    **argv)
135 {
136         dm_sessid_t      sid = DM_NO_SESSION;
137         dm_off_t        startoff = 0;           /* starting offset */
138         u_int           nelem = 100;
139         char            *pathname;
140         void            *hanp;
141         size_t           hlen;
142         dm_stat_t       sbuf;
143         char            *name;
144         int             opt;
145
146         Progname = strrchr(argv[0], '/');
147         if (Progname) {
148                 Progname++;
149         } else {
150                 Progname = argv[0];
151         }
152
153         /* Crack and validate the command line options. */
154
155         while ((opt = getopt(argc, argv, "Dn:o:s:")) != EOF) {
156                 switch(opt) {
157                 case 'D':
158                         Dflag++;
159                         break;
160                 case 'n':
161                         nelem = atol(optarg);
162                         break;
163                 case 'o':
164                         startoff = atoll(optarg);
165                         break;
166                 case 's':
167                         sid = atol(optarg);
168                         break;
169                 case '?':
170                         usage();
171                 }
172         }
173         if (optind + 1 != argc)
174                 usage();
175         pathname = argv[optind];
176
177         if (dm_init_service(&name)) {
178                 fprintf(stderr, "dm_init_service failed, %s\n",
179                         strerror(errno));
180                 exit(1);
181         }
182
183         if (sid == DM_NO_SESSION)
184                 find_test_session(&sid);
185
186         /* Get the file's handle and verify that it is a regular file. */
187
188         if (dm_path_to_handle(pathname, &hanp, &hlen)) {
189                 fprintf(stderr, "can't get handle for %s\n", pathname);
190                 exit(1);
191         }
192         if (dm_get_fileattr(sid, hanp, hlen, DM_NO_TOKEN, DM_AT_STAT, &sbuf)) {
193                 fprintf(stderr, "dm_get_fileattr failed\n");
194                 exit(1);
195         }
196         if (!S_ISREG(sbuf.dt_mode)) {
197                 fprintf(stderr, "%s is not a regular file\n", pathname);
198                 exit(1);
199         }
200
201         /* Print the allocation. */
202
203         if (print_alloc(sid, hanp, hlen, pathname, startoff, nelem))
204                 exit(1);
205
206         dm_handle_free(hanp, hlen);
207         exit(0);
208 }
209
210
211 static int
212 print_alloc(
213         dm_sessid_t     sid,
214         void            *hanp,
215         size_t          hlen,
216         char            *pathname,
217         dm_off_t        startoff,
218         u_int           nelem)
219 {
220         dm_off_t        endoff;
221         dm_extent_t     *extent;
222         u_int           nelemp;
223         u_int           num = 0;
224         u_int           i;
225         char            *type = NULL;
226         int             rc;
227
228         fprintf(stdout, "%s: starting offset %lld\n", pathname,
229                 (long long) startoff);
230
231         /* Allocate space for the number of extents requested by the user. */
232
233         if ((extent = malloc(nelem * sizeof(*extent))) == NULL) {
234                 fprintf(stderr, "can't malloc extent structures\n");
235                 return(1);
236         }
237
238         rc = 1;
239         endoff = startoff;
240
241         while (rc != 0) {
242                 rc = dm_get_allocinfo(sid, hanp, hlen, DM_NO_TOKEN, &startoff,
243                         nelem, extent, &nelemp);
244
245                 if (rc < 0) {
246                         fprintf(stderr, "dm_get_allocinfo failed, %s\n",
247                                 strerror(errno));
248                         return(1);
249                 }
250
251                 fprintf(stdout, "\treturned %d, nelemp %d\n", rc, nelemp);
252                 if (Dflag && nelemp)
253                         fprintf(stdout, " ex_type    ex_offset       ex_length\n");
254
255                 /* Note: it is possible for nelemp to be zero! */
256
257                 for (i = 0; i < nelemp; i++) {
258                         /* The extent must either be reserved space or a hole.
259                         */
260
261                         switch (extent[i].ex_type) {
262                         case DM_EXTENT_RES:
263                                 type = "resv";
264                                 break;
265                         case DM_EXTENT_HOLE:
266                                 type = "hole";
267                                 break;
268                         default:
269                                 fprintf(stderr, "invalid extent type %d\n",
270                                         extent[i].ex_type);
271                                 return(1);
272                         }
273
274                         if (!Dflag) {
275                                 fprintf(stdout, "\t%d: [%lld..%lld]: %s", num,
276                                         (long long) extent[i].ex_offset / 512,
277                                         (long long) (extent[i].ex_offset +
278                                         extent[i].ex_length - 1) / 512, type);
279                                 if ((extent[i].ex_offset % 512 != 0) ||
280                                     (endoff % 512 != 0)) {
281                                         fprintf(stdout, "\t[%lld..%lld]\n",
282                                                 (long long) extent[i].ex_offset % 512,
283                                                 (long long) (endoff-1) % 512);
284                                 } else {
285                                         fprintf(stdout, "\n");
286                                 }
287                         } else {
288                                 fprintf(stdout, "%5s    %13lld  %13lld\n",
289                                         type, (long long) extent[i].ex_offset,
290                                         (long long) extent[i].ex_length);
291                         }
292
293                         /* The ex_offset of the first extent should match the
294                            'startoff' specified by the caller.  The ex_offset
295                            in subsequent extents should always match
296                            (ex_offset + ex_length) of the previous extent,
297                            and should always start on a 512 byte boundary.
298                         */
299
300                         if (extent[i].ex_offset != endoff) {
301                                 fprintf(stderr, "new extent (%lld)is not "
302                                         "adjacent to previous one (%lld)\n",
303                                         (long long) extent[i].ex_offset,
304                                         (long long) endoff);
305                                 return(1);
306                         }
307                         if (num && (extent[i].ex_offset % 512) != 0) {
308                                 fprintf(stderr, "non-initial ex_offset (%lld) "
309                                         "is not a 512-byte multiple\n",
310                                         (long long) extent[i].ex_offset);
311                                 return(1);
312                         }
313
314                         /* Non-initial extents should have ex_length values
315                            that are an even multiple of 512.  The initial
316                            extent should be a multiple of 512 less the offset
317                            into the starting 512-byte block.
318                         */
319
320                         if (((extent[i].ex_offset % 512) + extent[i].ex_length) % 512 != 0) { 
321                                 fprintf(stderr, "ex_length is incorrect based "
322                                         "upon the ex_offset\n");
323                                 return(1);
324                         }
325
326                         endoff = extent[i].ex_offset + extent[i].ex_length;
327                         num++;          /* count of extents printed */
328                 }
329
330                 /* If not yet at end of file, the startoff parameter should
331                    match the ex_offset plus ex_length of the last extent
332                    retrieved.
333                 */
334
335                 if (rc && startoff != endoff) {
336                         fprintf(stderr, "startoff is %lld, should be %lld\n",
337                                 (long long) startoff, (long long) endoff);
338                         return(1);
339                 }
340
341                 /* If we are at end of file, the last extent should be a
342                    hole.
343                 */
344
345                 if (!rc && type && strcmp(type, "hole")) {
346                         fprintf(stderr, "file didn't end with a hole\n");
347                         return(1);
348                 }
349         }
350
351         /* At end of file, startoff should always equal zero. */
352
353         if (startoff != 0) {
354                 fprintf(stderr, "ERROR: startoff was not zero at end of file\n");
355                 return(1);
356         }
357         return(0);
358 }