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