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