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