f6a3335013fdcb5695dbb5aefa897c113eba5288
[xfstests-dev.git] / dmapi / src / common / lib / util.c
1 /*
2  * Utility routines
3  *
4  * This code was written by Peter Lawthers, and placed in the public
5  * domain for use by DMAPI implementors and app writers.
6  *
7  * Standard disclaimer:
8  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
9  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
10  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
11  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
12  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
14  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
15  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
16  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
17  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
18  * SUCH DAMAGE.
19  */
20
21 #include <sys/types.h>
22 #include <sys/stat.h>
23
24 #include <unistd.h>
25 #include <stdarg.h>
26
27 #include <lib/hsm.h>
28
29 #include <string.h>
30 #include <time.h>
31 #ifdef linux
32 #include <stdint.h>
33 #define S_IAMB (S_IRWXU|S_IRWXG|S_IRWXO)
34 #endif
35
36 #define S_MASK  (S_ISUID|S_ISGID|S_ISVTX|S_IAMB)
37
38 extern char     *Progname;
39 extern int       errno;
40
41 void             err_msg(char *, ...);
42 void             errno_msg(char *, ...);
43
44 struct ev_name_to_value ev_names[] = {
45         { "DM_EVENT_CANCEL",            DM_EVENT_CANCEL         },
46         { "DM_EVENT_MOUNT",             DM_EVENT_MOUNT          },
47         { "DM_EVENT_PREUNMOUNT",        DM_EVENT_PREUNMOUNT     },
48         { "DM_EVENT_UNMOUNT",           DM_EVENT_UNMOUNT        },
49         { "DM_EVENT_DEBUT",             DM_EVENT_DEBUT          },
50         { "DM_EVENT_CREATE",            DM_EVENT_CREATE         },
51         { "DM_EVENT_CLOSE",             DM_EVENT_CLOSE          },
52         { "DM_EVENT_POSTCREATE",        DM_EVENT_POSTCREATE     },
53         { "DM_EVENT_REMOVE",            DM_EVENT_REMOVE         },
54         { "DM_EVENT_POSTREMOVE",        DM_EVENT_POSTREMOVE     },
55         { "DM_EVENT_RENAME",            DM_EVENT_RENAME         },
56         { "DM_EVENT_POSTRENAME",        DM_EVENT_POSTRENAME     },
57         { "DM_EVENT_LINK",              DM_EVENT_LINK           },
58         { "DM_EVENT_POSTLINK",          DM_EVENT_POSTLINK       },
59         { "DM_EVENT_SYMLINK",           DM_EVENT_SYMLINK        },
60         { "DM_EVENT_POSTSYMLINK",       DM_EVENT_POSTSYMLINK    },
61         { "DM_EVENT_READ",              DM_EVENT_READ           },
62         { "DM_EVENT_WRITE",             DM_EVENT_WRITE          },
63         { "DM_EVENT_TRUNCATE",          DM_EVENT_TRUNCATE       },
64         { "DM_EVENT_ATTRIBUTE",         DM_EVENT_ATTRIBUTE      },
65         { "DM_EVENT_DESTROY",           DM_EVENT_DESTROY        },
66         { "DM_EVENT_NOSPACE",           DM_EVENT_NOSPACE        },
67         { "DM_EVENT_USER",              DM_EVENT_USER           }
68 };
69
70 int     ev_namecnt = sizeof(ev_names) / sizeof(ev_names[0]);
71
72 dm_eventtype_t
73 ev_name_to_value(
74         char            *name)
75 {
76         int             i;
77
78         for (i = 0; i < ev_namecnt; i++) {
79                 if (!strcmp(name, ev_names[i].name)) 
80                         return(ev_names[i].value);
81         }
82         return(DM_EVENT_INVALID);
83 }
84
85 char *
86 ev_value_to_name(
87         dm_eventtype_t  event)
88 {
89         static char             buffer[100];
90         int             i;
91
92         for (i = 0; i < ev_namecnt; i++) {
93                 if (event == ev_names[i].value)
94                         return(ev_names[i].name);
95         }
96         sprintf(buffer, "Unknown Event Number %d\n", event);
97         return(buffer);
98 }
99
100
101
102 struct rt_name_to_value rt_names[] = {
103         { "DM_RIGHT_NULL",              DM_RIGHT_NULL           },
104         { "DM_RIGHT_SHARED",            DM_RIGHT_SHARED         },
105         { "DM_RIGHT_EXCL",              DM_RIGHT_EXCL           }
106 };
107
108 int     rt_namecnt = sizeof(rt_names) / sizeof(rt_names[0]);
109
110 int
111 rt_name_to_value(
112         char            *name,
113         dm_right_t      *rightp)
114 {
115         int             i;
116
117         for (i = 0; i < rt_namecnt; i++) {
118                 if (!strcmp(name, rt_names[i].name)) {
119                         *rightp = rt_names[i].value;
120                         return(0);
121                 }
122         }
123         return(1);
124 }
125
126
127 char *
128 rt_value_to_name(
129         dm_right_t      right)
130 {
131         int             i;
132
133         for (i = 0; i < rt_namecnt; i++) {
134                 if (right == rt_names[i].value)
135                         return(rt_names[i].name);
136         }
137         return(NULL);
138 }
139  
140  
141 /*
142  * Convert a handle from (possibly) binary to ascii. 
143  */
144 void
145 hantoa(
146         void    *hanp,
147         size_t   hlen,
148         char    *handle_str)
149 {
150         int     i;
151         u_char  *cp;
152
153         cp = (u_char *)hanp;
154         for (i=0;i<hlen; i++)  {
155                 sprintf(handle_str, "%.2x", *cp++);
156                 handle_str += 2;
157         }
158         *handle_str = '\0';     /* Null-terminate to make it printable */
159 }
160
161 /*
162  * Convert a handle from ascii back to it's native binary representation
163  */
164
165 int
166 atohan(
167         char    *handle_str,
168         void    **hanpp,
169         size_t   *hlenp) 
170 {
171         u_char  handle[HANDLE_LEN];
172         char    cur_char[3];
173         int     i = 0;
174         u_long  num;
175
176         if (strlen(handle_str) > HANDLE_LEN * 2){
177                 return(EBADF);
178         }
179
180         while (*handle_str && *(handle_str + 1)) {
181                 if (i == HANDLE_LEN){
182                         return(EBADF);
183                 }
184                 if( ! (isxdigit(*handle_str) && (isxdigit(*(handle_str +1))))) {
185                         return(EBADF);
186                 }
187                 cur_char[0] = *handle_str;
188                 cur_char[1] = *(handle_str + 1);
189                 cur_char[2] = '\0';
190                 num = strtol(cur_char, (char **)0, 16);
191                 handle[i++] = num & 0xff;
192                 handle_str += 2;
193         }
194         if (*handle_str){
195                 return(EBADF);
196         }
197         *hlenp = i;
198         if ((*hanpp = malloc(*hlenp)) == NULL)
199                 return(ENOMEM);
200         memcpy(*hanpp, handle, *hlenp);
201         return(0);
202 }
203
204
205 int
206 opaque_to_handle(
207         char            *name,
208         void            **hanpp,
209         size_t          *hlenp)
210 {
211         if (atohan(name, hanpp, hlenp)) {
212                 /* not a handle */
213         } else if (dm_handle_is_valid(*hanpp, *hlenp) == DM_FALSE) {
214                 dm_handle_free(*hanpp, *hlenp);
215                 /* not a handle */
216         } else {
217                 return(0);
218         }
219
220         /* Perhaps it is a pathname */
221
222         if (dm_path_to_handle(name, hanpp, hlenp)) {
223                 return(errno);
224         }
225         return(0);
226 }
227
228
229 void
230 print_handle(
231         void    *hanp,
232         size_t   hlen)
233 {
234         char    handle_str[HANDLE_STR];
235
236         if (hlen > HANDLE_LEN)  {
237                 printf("-- invalid hlen length %zd --\n", hlen);
238                 return;
239         }
240
241         printf("print_handle: ");
242         printf("%zd\t", hlen);
243         hantoa(hanp, hlen, handle_str);
244         printf("%s\n ", handle_str);
245 }
246
247 void
248 print_victim(
249         void            *hanp, 
250         size_t           hlen,
251         dm_off_t         fsize)
252 {
253         char    handle_str[HANDLE_STR];
254
255         if (hlen > HANDLE_LEN)  {
256                 printf("-- invalid length --\n");
257                 return;
258         }
259
260         printf("%zd\t", hlen);
261         hantoa(hanp, hlen, handle_str);
262         printf("%s ", handle_str);
263         printf("\t%lld \n", (long long) fsize);
264 }
265
266
267 /*
268  * Print out a simple error message, and include the errno
269  * string with it.
270  */
271 void
272 errno_msg(char *fmt, ...)
273 {
274         va_list         ap;
275         int             old_errno;
276
277         old_errno = errno;
278         fprintf(stderr, "%s: ", Progname);
279
280         va_start(ap, fmt );
281         vfprintf(stderr, fmt, ap);
282         va_end(ap);
283
284         errno = old_errno;
285         perror("\n\tError");
286 }
287
288 /*
289  * Print out a simple error message
290  */
291 void
292 err_msg(char *fmt, ...)
293 {
294         va_list         ap;
295         
296         fprintf(stderr, "%s: ", Progname);
297
298         va_start(ap, fmt );
299         vfprintf(stderr, fmt, ap);
300         va_end(ap);
301
302 }
303
304
305 /*
306  * Initialize the interface to the DMAPI
307  */
308 int
309 setup_dmapi(dm_sessid_t  *sidp)
310 {
311         char    *cp;
312
313         if (dm_init_service(&cp) == -1)  {
314                 err_msg("%s/%d: Can't init dmapi\n", __FILE__, __LINE__);
315                 return(1);
316         }
317         if (strcmp(cp, DM_VER_STR_CONTENTS)) {
318                 err_msg("%s/%d: Compiled for a different version\n", __FILE__, __LINE__);
319                 return(1);
320         }
321
322         find_test_session(sidp);
323         return(0);
324 }
325
326 /*
327  * Get the file's change indicator
328  */
329 int
330 get_dmchange(
331         dm_sessid_t      sid,
332         void            *hanp, 
333         size_t           hlen, 
334         dm_token_t       token,
335         u_int           *change_start)
336 {
337         int             error;
338         dm_stat_t       statbuf;
339
340
341         error = dm_get_fileattr(sid, hanp, hlen, token, DM_AT_CFLAG, &statbuf);
342         if (error == -1) {
343                 errno_msg("%s/%d: Can't stat file (%d)", __FILE__, __LINE__, errno);
344                 return(1);
345         }
346         *change_start = statbuf.dt_change;
347         return(0);
348 }
349
350
351 /*
352  * Write a file's data to the staging file. We write the file out
353  * in 4meg chunks.
354  */
355 int
356 save_filedata(
357         dm_sessid_t      sid, 
358         void            *hanp, 
359         size_t           hlen, 
360         int              stg_fd, 
361         dm_size_t        fsize)
362 {
363         char            *filebuf;
364         int              i, nbufs;
365         int              retval;
366         dm_ssize_t       nread, lastbuf;
367         ssize_t          nwrite;
368         dm_off_t         off;
369
370         nbufs  = fsize / CHUNKSIZE;
371         off    = 0;
372         retval = 0;
373         filebuf = malloc(CHUNKSIZE);
374         if (filebuf == NULL) {
375                 err_msg("%s/%d: Can't alloc memory for file buffer\n", __FILE__, __LINE__);
376                 goto out;
377         }
378
379         for (i = 0; i<nbufs; i++) {
380                 nread = dm_read_invis(sid, hanp, hlen, DM_NO_TOKEN, off, 
381                                         (dm_ssize_t)CHUNKSIZE, filebuf);
382                 if (nread != CHUNKSIZE) {
383 #ifdef  __sgi
384                         errno_msg("%s/%d: invis read err: got %lld, expected %lld, buf %d",
385 #else
386                         errno_msg("%s/%d: invis read err: got %d, expected %d, buf %d",
387 #endif
388                                 __FILE__, __LINE__,
389                                 nread, (dm_ssize_t)CHUNKSIZE, i);
390                         retval = 1;
391                         goto out;
392                 }
393                 off += nread;
394
395                 nwrite = write(stg_fd, filebuf, CHUNKSIZE);
396                 if (nwrite != CHUNKSIZE) {
397                         errno_msg("%s/%d: write err %d, expected %d, buf %d",
398                                   __FILE__, __LINE__,
399                                   nwrite, CHUNKSIZE, i);
400                         retval = 1;
401                         goto out;
402                 }
403         }
404
405         lastbuf = fsize % CHUNKSIZE;
406         nread  = dm_read_invis(sid, hanp, hlen, DM_NO_TOKEN, off, 
407                                 (dm_ssize_t)lastbuf, filebuf);
408         if (nread != lastbuf) {
409 #ifdef  __sgi
410                 errno_msg("%s/%d: invis read error- got %lld, expected %lld, last buf",
411 #else
412                 errno_msg("%s/%d: invis read error- got %d, expected %d, last buf",
413 #endif
414                                 __FILE__, __LINE__,
415                                 nread, lastbuf);
416                 retval = 1;
417                 goto out;
418         }
419
420         nwrite = write(stg_fd, filebuf, (int)lastbuf);
421         if (nwrite != lastbuf) {
422 #ifdef  __sgi
423                 errno_msg("%s/%d: write error %d, expected %lld, last buffer", 
424 #else
425                 errno_msg("%s/%d: write error %d, expected %d, last buffer", 
426 #endif
427                                 __FILE__, __LINE__,
428                                 nwrite, lastbuf);
429                 retval = 1;
430         }
431 out:
432         if (filebuf)
433                 free(filebuf);
434
435         close(stg_fd);
436         return(retval);
437 }
438
439
440
441 /*
442  * Read a file's data from the staging file. 
443  * Since we only have the staging file handle (not a file descriptor)
444  * we use dm_read_invis() to read the data. 
445  *
446  * We stage the entire file in, regardless of how much was asked for, 
447  * starting at the faulting offset.
448  */
449 int
450 restore_filedata(
451         dm_sessid_t      sid, 
452         void            *hanp, 
453         size_t           hlen, 
454         dm_token_t       token,
455         void            *stg_hanp,
456         size_t           stg_hlen,
457         dm_off_t         off)
458 {
459         char            *filebuf;
460         int              i, nbufs;
461         int              error, retval;
462         dm_ssize_t       nread, nwrite, lastbuf;
463         dm_off_t         fsize;
464         dm_stat_t        dm_statbuf;
465
466         error = dm_get_fileattr(sid, hanp, hlen, token, DM_AT_STAT,
467                                 &dm_statbuf);
468         if (error == -1) {
469                 errno_msg("%s/%d: Can't get dm stats of file (%d)", __FILE__, __LINE__, errno);
470                 return(1);
471         }
472
473         fsize  = dm_statbuf.dt_size;
474         nbufs  = fsize / CHUNKSIZE;
475         retval = 0;
476
477         filebuf = malloc(CHUNKSIZE);
478         if (filebuf == NULL) {
479                 err_msg("%s/%d: Can't alloc memory for file buffer\n", __FILE__, __LINE__);
480                 goto out;
481         }
482
483         for (i = 0; i<nbufs; i++) {
484                 nread = dm_read_invis(sid, stg_hanp, stg_hlen, DM_NO_TOKEN, 
485                                         off, (dm_ssize_t)CHUNKSIZE, filebuf);
486                 if (nread != CHUNKSIZE) {
487                         errno_msg("%s/%d: invis read err: got %d, expected %d, buf %d",
488                                   __FILE__, __LINE__,
489                                 nread, CHUNKSIZE, i);
490                         retval = 1;
491                         goto out;
492                 }
493
494                 nwrite = dm_write_invis(sid, hanp, hlen, token, 0, off, nread,
495                                         filebuf);
496                 if (nwrite != nread) {
497                         errno_msg("%s/%d: write error -got %d, expected %d, buf %d", 
498                                   __FILE__, __LINE__,
499                                 nwrite, nread, i);
500                         retval = 1;
501                         goto out;
502                 }
503                 off += CHUNKSIZE;
504         }
505
506         lastbuf = fsize % CHUNKSIZE;
507         nread  = dm_read_invis(sid, stg_hanp, stg_hlen, DM_NO_TOKEN, off, 
508                                 (dm_ssize_t)lastbuf, filebuf);
509         if (nread != lastbuf) {
510                 errno_msg("%s/%d: invis read error- got %d, expected %d, last buf",
511                                 __FILE__, __LINE__,
512                                 nread, lastbuf);
513                 retval = 1;
514                 goto out;
515         }
516
517         nwrite = dm_write_invis(sid, hanp, hlen, token, 0, off, lastbuf, filebuf);
518         if (nwrite != lastbuf) {
519                 errno_msg("%s/%d: write error - got %d, expected %d, last buffer", 
520                                 __FILE__, __LINE__,
521                                 nwrite, lastbuf);
522                 retval = 1;
523         }
524 out:
525         if (filebuf)
526                 free(filebuf);
527         return(retval);
528 }
529
530
531 extern mode_t
532 field_to_mode(
533         mode_t  mode)
534 {
535         switch (mode & S_IFMT) {
536
537         case S_IFBLK:
538                 return(S_IFBLK);
539
540         case S_IFREG:
541                 return(S_IFREG);
542
543         case S_IFDIR:
544                 return(S_IFDIR);
545
546         case S_IFCHR:
547                 return(S_IFCHR);
548
549         case S_IFIFO:
550                 return(S_IFIFO);
551
552         case S_IFLNK:
553                 return(S_IFLNK);
554
555         case S_IFSOCK:
556                 return(S_IFSOCK);
557
558         default:
559                 return(0);
560         }
561 }
562
563
564 extern int
565 validate_state(
566         dm_stat_t       *dmstat,
567         char            *pathname,
568         int             report_errors)
569 {
570         struct  stat    statb;
571         int             errors = 0;
572
573         /* Get the stat block for the file. */
574
575         if (lstat(pathname, &statb)) {
576                 perror("stat failed");
577                 exit(1);
578         }
579
580         /* Compare its fields against the dm_stat_t structure. */
581
582         if (dmstat->dt_dev != statb.st_dev) {
583                 if (report_errors) {
584                         fprintf(stdout, "ERROR:dmstat->dt_dev 0x%llx, "
585                                 "statb.st_dev 0x%llx\n",
586                                 (unsigned long long) dmstat->dt_dev,
587                                 (unsigned long long) statb.st_dev);
588                 }
589                 errors++;
590         }
591         if (dmstat->dt_ino != statb.st_ino) {
592                 if (report_errors) {
593                         fprintf(stdout, "ERROR:dmstat->dt_ino %llx, "
594                                 "statb.st_ino %llx\n",
595                                 (unsigned long long) dmstat->dt_ino,
596                                 (unsigned long long) statb.st_ino);
597                 }
598                 errors++;
599         }
600         if ((dmstat->dt_mode & S_IFMT) != field_to_mode(statb.st_mode)) {
601                 if (report_errors) {
602                         fprintf(stdout, "ERROR:dmstat->dt_mode (mode) %s, "
603                                 "statb.st_mode (mode) %s\n",
604                                 mode_to_string(dmstat->dt_mode),
605                                 mode_to_string(field_to_mode(statb.st_mode)));
606                 }
607                 errors++;
608         }
609         if ((dmstat->dt_mode & S_MASK) != (statb.st_mode & S_MASK)) {
610                 if (report_errors) {
611                         fprintf(stdout, "ERROR:dmstat->dt_mode (perm) 0%o, "
612                                 "statb.st_mode (perm) 0%o\n",
613                                 dmstat->dt_mode & S_MASK,
614                                 statb.st_mode & S_MASK);
615                 }
616                 errors++;
617         }
618         if (dmstat->dt_nlink != statb.st_nlink) {
619                 if (report_errors) {
620                         fprintf(stdout, "ERROR:dmstat->dt_nlink %u, "
621                                 "statb.st_nlink %u\n",
622                                 (unsigned int) dmstat->dt_nlink,
623                                 (unsigned int) statb.st_nlink);
624                 }
625                 errors++;
626         }
627         if (dmstat->dt_uid !=  statb.st_uid) {
628                 if (report_errors) {
629                         fprintf(stdout, "ERROR:dmstat->dt_uid %d, "
630                                 "statb.st_uid %d\n", dmstat->dt_uid,
631                                 statb.st_uid);
632                 }
633                 errors++;
634         }
635         if (dmstat->dt_gid != statb.st_gid) {
636                 if (report_errors) {
637                         fprintf(stdout, "ERROR:dmstat->dt_gid %d, "
638                                 "statb.st_gid %d\n", dmstat->dt_gid,
639                                 statb.st_gid);
640                 }
641                 errors++;
642         }
643         if (dmstat->dt_rdev != statb.st_rdev) {
644                 if (report_errors) {
645                         fprintf(stdout, "ERROR:dmstat->dt_rdev 0x%llx, "
646                                 "statb.st_rdev 0x%llx\n",
647                                 (unsigned long long) dmstat->dt_rdev,
648                                 (unsigned long long) statb.st_rdev);
649                 }
650                 errors++;
651         }
652         if (dmstat->dt_size != statb.st_size) {
653                 if (report_errors) {
654                         fprintf(stdout, "ERROR:dmstat->dt_size %lld, "
655                                 "statb.st_size %lld\n",
656                                 (long long) dmstat->dt_size,
657                                 (long long) statb.st_size);
658                 }
659                 errors++;
660         }
661         if (dmstat->dt_atime != statb.st_atime) {
662                 if (report_errors) {
663                         fprintf(stdout, "ERROR:dmstat->dt_atime %ld, "
664                                 "statb.st_atime %ld\n", dmstat->dt_atime,
665                                 statb.st_atime);
666                 }
667                 errors++;
668         }
669         if (dmstat->dt_mtime != statb.st_mtime) {
670                 if (report_errors) {
671                         fprintf(stdout, "ERROR:dmstat->dt_mtime %ld, "
672                                 "statb.st_mtime %ld\n", dmstat->dt_mtime,
673                                 statb.st_mtime);
674                 }
675                 errors++;
676         }
677         if (dmstat->dt_ctime != statb.st_ctime) {
678                 if (report_errors) {
679                         fprintf(stdout, "ERROR:dmstat->dt_ctime %ld, "
680                                 "statb.st_ctime %ld\n", dmstat->dt_ctime,
681                                 statb.st_ctime);
682                 }
683                 errors++;
684         }
685         if (dmstat->dt_dtime != statb.st_ctime) {
686                 if (report_errors) {
687                         fprintf(stdout, "ERROR:dmstat->dt_dtime %ld, "
688                                 "statb.st_ctime %ld\n", dmstat->dt_dtime,
689                                 statb.st_ctime);
690                 }
691                 errors++;
692         }
693         if (dmstat->dt_blksize != statb.st_blksize) {
694                 if (report_errors) {
695                         fprintf(stdout, "ERROR:dmstat->dt_blksize %d, "
696                                 "statb.st_blksize %ld\n", dmstat->dt_blksize,
697                                 statb.st_blksize);
698                 }
699                 errors++;
700         }
701         if (dmstat->dt_blocks != statb.st_blocks) {
702                 if (report_errors) {
703                         fprintf(stdout, "ERROR:dmstat->dt_blocks %lld, "
704                                 "statb.st_blocks %lld\n",
705                                 (long long) dmstat->dt_blocks,
706                                 (long long) statb.st_blocks);
707                 }
708                 errors++;
709         }
710
711         if (errors && report_errors)
712                 fprintf(stdout, "There were %d differences\n", errors);
713         return(errors);
714 }
715
716
717 extern char *
718 date_to_string(
719         time_t          timeval)
720 {
721 static  char            buffer[21];
722         char            *tmstr;
723
724         if (timeval == (time_t)0) {
725                 strcpy(buffer, "0");
726         } else {
727                 tmstr = asctime(localtime(&timeval));
728                 tmstr += 4;
729                 strncpy(buffer, tmstr, 20);
730                 buffer[20] = '\0';
731         }
732         return(buffer);
733 }
734
735
736 extern char *
737 mode_to_string(
738         mode_t  mode)
739 {
740 static  char            buffer[256];
741
742         switch (mode & S_IFMT) {
743
744         case S_IFBLK:
745                 return("S_IFBLK");
746
747         case S_IFREG:
748                 return("S_IFREG");
749
750         case S_IFDIR:
751                 return("S_IFDIR");
752
753         case S_IFCHR:
754                 return("S_IFCHR");
755
756         case S_IFIFO:
757                 return("S_IFIFO");
758
759         case S_IFLNK:
760                 return("S_IFLNK");
761
762         case S_IFSOCK:
763                 return("S_IFSOCK");
764
765         default:
766                 sprintf(buffer, "Invalid mode 0%o", mode & S_IFMT);
767                 return(buffer);
768         }
769 }
770
771
772 extern char *
773 emask_to_string(
774         dm_eventset_t   emask)
775 {
776 static  char            buffer[256];
777         char            *name;
778         int             len = 0;
779         int             i;
780
781         for (i = 0; i < 32; i++) {
782                 if (!DMEV_ISSET(i, emask))
783                         continue;
784
785                 switch (i) {
786                 case DM_EVENT_CREATE:
787                         name = "DM_EVENT_CREATE";
788                         break;
789                 case DM_EVENT_POSTCREATE:
790                         name = "DM_EVENT_POSTCREATE";
791                         break;
792                 case DM_EVENT_REMOVE:
793                         name = "DM_EVENT_REMOVE";
794                         break;
795                 case DM_EVENT_POSTREMOVE:
796                         name = "DM_EVENT_POSTREMOVE";
797                         break;
798                 case DM_EVENT_RENAME:
799                         name = "DM_EVENT_RENAME";
800                         break;
801                 case DM_EVENT_POSTRENAME:
802                         name = "DM_EVENT_POSTRENAME";
803                         break;
804                 case DM_EVENT_LINK:
805                         name = "DM_EVENT_LINK";
806                         break;
807                 case DM_EVENT_POSTLINK:
808                         name = "DM_EVENT_POSTLINK";
809                         break;
810                 case DM_EVENT_SYMLINK:
811                         name = "DM_EVENT_SYMLINK";
812                         break;
813                 case DM_EVENT_POSTSYMLINK:
814                         name = "DM_EVENT_POSTSYMLINK";
815                         break;
816                 case DM_EVENT_READ:
817                         name = "DM_EVENT_READ";
818                         break;
819                 case DM_EVENT_WRITE:
820                         name = "DM_EVENT_WRITE";
821                         break;
822                 case DM_EVENT_TRUNCATE:
823                         name = "DM_EVENT_TRUNCATE";
824                         break;
825                 case DM_EVENT_ATTRIBUTE:
826                         name = "DM_EVENT_ATTRIBUTE";
827                         break;
828                 case DM_EVENT_DESTROY:
829                         name = "DM_EVENT_DESTROY";
830                         break;
831                 default:
832                         fprintf(stderr, "Unknown event type %d\n", i);
833                         exit(1);
834                 }
835                 sprintf(buffer + len, "%c%s", (len ? '|' : '('), name);
836                 len = strlen(buffer);
837         }
838
839         if (len == 0) {
840                 sprintf(buffer, "(none)");
841         } else {
842                 sprintf(buffer + len, ")");
843         }
844         return(buffer);
845 }
846
847
848 #if defined(__sgi) || defined(linux)
849
850 extern char *
851 xflags_to_string(
852         u_int           xflags)
853 {
854 static  char            buffer[256];
855         int             len = 0;
856
857         if (xflags & ~(DM_XFLAG_REALTIME|DM_XFLAG_PREALLOC|DM_XFLAG_HASATTR)) {
858                 sprintf(buffer, "Invalid xflags 0%o", xflags);
859                 return(buffer);
860         }
861
862         if (xflags & DM_XFLAG_REALTIME) {
863                 sprintf(buffer + len, "%cREALTIME", (len ? '|' : '('));
864                 len = strlen(buffer);
865         }
866         if (xflags & DM_XFLAG_PREALLOC) {
867                 sprintf(buffer + len, "%cPREALLOC", (len ? '|' : '('));
868                 len = strlen(buffer);
869         }
870         if (xflags & DM_XFLAG_HASATTR) {
871                 sprintf(buffer + len, "%cHASATTR", (len ? '|' : '('));
872                 len = strlen(buffer);
873         }
874         if (len == 0) {
875                 sprintf(buffer, "(none)");
876         } else {
877                 sprintf(buffer + len, ")");
878         }
879         return(buffer);
880 }
881
882 #endif
883
884
885 extern void
886 print_state(
887         dm_stat_t       *dmstat)
888 {
889         /* Print all the stat block fields. */
890
891         fprintf(stdout, "dt_dev         0x%llx\n",
892                 (unsigned long long) dmstat->dt_dev);
893         fprintf(stdout, "dt_ino         %llx\n",
894                 (unsigned long long) dmstat->dt_ino);
895         fprintf(stdout, "dt_mode (type) %s\n",
896                 mode_to_string(dmstat->dt_mode));
897         fprintf(stdout, "dt_mode (perm) 0%o\n", dmstat->dt_mode & S_MASK);
898         fprintf(stdout, "dt_nlink       %d\n",  dmstat->dt_nlink);
899         fprintf(stdout, "dt_uid         %d\n",  dmstat->dt_uid);
900         fprintf(stdout, "dt_gid         %d\n", dmstat->dt_gid);
901         fprintf(stdout, "dt_rdev        0x%llx\n",
902                 (unsigned long long) dmstat->dt_rdev);
903         fprintf(stdout, "dt_size        %lld\n",
904                 (unsigned long long) dmstat->dt_size);
905
906         fprintf(stdout, "dt_atime       %s\n",
907                 date_to_string(dmstat->dt_atime));
908         fprintf(stdout, "dt_mtime       %s\n",
909                 date_to_string(dmstat->dt_mtime));
910         fprintf(stdout, "dt_ctime       %s\n",
911                 date_to_string(dmstat->dt_ctime));
912
913         fprintf(stdout, "dt_blksize     %d\n", dmstat->dt_blksize);
914         fprintf(stdout, "dt_blocks      %lld\n", (long long) dmstat->dt_blocks);
915
916 #if defined(__sgi) || defined(linux)
917         fprintf(stdout, "dt_xfs_igen    %d\n",  dmstat->dt_xfs_igen);
918         fprintf(stdout, "dt_xfs_xflags  %s\n",
919                 xflags_to_string(dmstat->dt_xfs_xflags));
920         fprintf(stdout, "dt_xfs_extsize %d\n", dmstat->dt_xfs_extsize);
921         fprintf(stdout, "dt_xfs_extents %d\n", dmstat->dt_xfs_extents);
922         fprintf(stdout, "dt_xfs_aextents %d\n", dmstat->dt_xfs_aextents);
923 #endif
924
925         fputc('\n', stdout);
926
927         /* Print all other fields. */
928
929         fprintf(stdout, "emask           %s\n",
930                 emask_to_string(dmstat->dt_emask));
931         fprintf(stdout, "nevents         %d\n", dmstat->dt_nevents);
932         fprintf(stdout, "pmanreg         %d\n", dmstat->dt_pmanreg);
933         fprintf(stdout, "pers            %d\n", dmstat->dt_pers);
934         fprintf(stdout, "dt_dtime        %s\n",
935                 date_to_string(dmstat->dt_dtime));
936         fprintf(stdout, "change          %d\n", dmstat->dt_change);
937 }
938
939
940 extern void
941 print_line(
942         dm_stat_t       *dmstat)
943 {
944         fprintf(stdout, "0x%llx|",  (unsigned long long) dmstat->dt_dev);
945         fprintf(stdout, "%llx|",  (unsigned long long) dmstat->dt_ino);
946         fprintf(stdout, "%s|", mode_to_string(dmstat->dt_mode));
947         fprintf(stdout, "0%o|", dmstat->dt_mode & S_MASK);
948         fprintf(stdout, "%d|",  dmstat->dt_nlink);
949         fprintf(stdout, "%d|",  dmstat->dt_uid);
950         fprintf(stdout, "%d|", dmstat->dt_gid);
951         fprintf(stdout, "0x%llx|", (unsigned long long) dmstat->dt_rdev);
952         fprintf(stdout, "%lld|", (long long) dmstat->dt_size);
953
954         fprintf(stdout, "%s|", date_to_string(dmstat->dt_atime));
955         fprintf(stdout, "%s|", date_to_string(dmstat->dt_mtime));
956         fprintf(stdout, "%s|", date_to_string(dmstat->dt_ctime));
957
958         fprintf(stdout, "%d|", dmstat->dt_blksize);
959         fprintf(stdout, "%lld|", (long long) dmstat->dt_blocks);
960
961         fprintf(stdout, "%d|",  dmstat->dt_xfs_igen);
962         fprintf(stdout, "%s|", xflags_to_string(dmstat->dt_xfs_xflags));
963         fprintf(stdout, "%d|", dmstat->dt_xfs_extsize);
964         fprintf(stdout, "%d|", dmstat->dt_xfs_extents);
965         fprintf(stdout, "%d|", dmstat->dt_xfs_aextents);
966
967         /* Print all other fields. */
968
969         fprintf(stdout, "%s|", emask_to_string(dmstat->dt_emask));
970         fprintf(stdout, "%d|", dmstat->dt_nevents);
971         fprintf(stdout, "%d|", dmstat->dt_pmanreg);
972         fprintf(stdout, "%d|", dmstat->dt_pers);
973         fprintf(stdout, "%s|", date_to_string(dmstat->dt_dtime));
974         fprintf(stdout, "%d", dmstat->dt_change);
975
976         fputc('\n', stdout);
977 }