1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
23 * All Rights Reserved
24 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 * Copyright 2024 MNX Cloud, Inc.
27 */
28
29 /*
30 * Portions of this source code were derived from Berkeley 4.3 BSD
31 * under license from the Regents of the University of California.
32 */
33
34 /*
35 * libfstyp module for pcfs
36 */
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/mntent.h>
40 #include <errno.h>
41 #include <sys/fs/pc_fs.h>
42 #include <sys/fs/pc_label.h>
43 #include <sys/fs/pc_dir.h>
44 #include <sys/stat.h>
45 #include <sys/vfs.h>
46 #include <sys/dkio.h>
47 #include <stdio.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <strings.h>
51 #include <sys/mnttab.h>
52 #include <locale.h>
53 #include <libfstyp_module.h>
54
55 #define PC_LABEL_SIZE 11
56
57 /* for the <sys/fs/pc_dir.h> PCDL_IS_LFN macro */
58 int enable_long_filenames = 1;
59
60 struct fstyp_fat16_bs {
61 uint8_t f_drvnum;
62 uint8_t f_reserved1;
63 uint8_t f_bootsig;
64 uint8_t f_volid[4];
65 uint8_t f_label[11];
66 uint8_t f_typestring[8];
67 };
68
69 struct fstyp_fat32_bs {
70 uint32_t f_fatlength;
71 uint16_t f_flags;
72 uint8_t f_major;
73 uint8_t f_minor;
74 uint32_t f_rootcluster;
75 uint16_t f_infosector;
76 uint16_t f_backupboot;
77 uint8_t f_reserved2[12];
78 uint8_t f_drvnum;
79 uint8_t f_reserved1;
80 uint8_t f_bootsig;
81 uint8_t f_volid[4];
82 uint8_t f_label[11];
83 uint8_t f_typestring[8];
84 };
85
86 typedef struct fstyp_pcfs {
87 int fd;
88 off_t offset;
89 nvlist_t *attr;
90 struct bootsec bs;
91 struct fstyp_fat16_bs bs16;
92 struct fstyp_fat32_bs bs32;
93 ushort_t bps;
94 int fattype;
95 char volume_label[PC_LABEL_SIZE + 1];
96
97 /* parameters derived or calculated per FAT spec */
98 ulong_t FATSz;
99 ulong_t TotSec;
100 ulong_t RootDirSectors;
101 ulong_t FirstDataSector;
102 ulong_t DataSec;
103 ulong_t CountOfClusters;
104 } fstyp_pcfs_t;
105
106
107 /* We should eventually make the structs "packed" so these won't be needed */
108 #define PC_BPSEC(h) ltohs((h)->bs.bps[0])
109 #define PC_RESSEC(h) ltohs((h)->bs.res_sec[0])
110 #define PC_NROOTENT(h) ltohs((h)->bs.rdirents[0])
111 #define PC_NSEC(h) ltohs((h)->bs.numsect[0])
112 #define PC_DRVNUM(h) (FSTYP_IS_32(h) ? (h)->bs32.f_drvnum : \
113 (h)->bs16.f_drvnum)
114 #define PC_VOLID(a) (FSTYP_IS_32(h) ? ltohi((h)->bs32.f_volid[0]) : \
115 ltohi((h)->bs16.f_volid[0]))
116 #define PC_LABEL_ADDR(a) (FSTYP_IS_32(h) ? \
117 &((h)->bs32.f_label[0]) : &((h)->bs16.f_label[0]))
118
119 #define FSTYP_IS_32(h) ((h)->fattype == 32)
120
121 #define FSTYP_MAX_CLUSTER_SIZE (64 * 1024) /* though officially 32K */
122 #define FSTYP_MAX_DIR_SIZE (65536 * 32)
123
124 static int read_bootsec(fstyp_pcfs_t *h);
125 static int valid_media(fstyp_pcfs_t *h);
126 static int well_formed(fstyp_pcfs_t *h);
127 static void calculate_parameters(fstyp_pcfs_t *h);
128 static void determine_fattype(fstyp_pcfs_t *h);
129 static void get_label(fstyp_pcfs_t *h);
130 static void get_label_16(fstyp_pcfs_t *h);
131 static void get_label_32(fstyp_pcfs_t *h);
132 static int next_cluster_32(fstyp_pcfs_t *h, int n);
133 static boolean_t dir_find_label(fstyp_pcfs_t *h, struct pcdir *d, int nent);
134 static int is_pcfs(fstyp_pcfs_t *h);
135 static int dumpfs(fstyp_pcfs_t *h, FILE *fout, FILE *ferr);
136 static int get_attr(fstyp_pcfs_t *h);
137
138 int fstyp_mod_init(int fd, off_t offset, fstyp_mod_handle_t *handle);
139 void fstyp_mod_fini(fstyp_mod_handle_t handle);
140 int fstyp_mod_ident(fstyp_mod_handle_t handle);
141 int fstyp_mod_get_attr(fstyp_mod_handle_t handle, nvlist_t **attrp);
142 int fstyp_mod_dump(fstyp_mod_handle_t handle, FILE *fout, FILE *ferr);
143
144 int
fstyp_mod_init(int fd,off_t offset,fstyp_mod_handle_t * handle)145 fstyp_mod_init(int fd, off_t offset, fstyp_mod_handle_t *handle)
146 {
147 struct fstyp_pcfs *h;
148
149 if ((h = calloc(1, sizeof (struct fstyp_pcfs))) == NULL) {
150 return (FSTYP_ERR_NOMEM);
151 }
152 h->fd = fd;
153 h->offset = offset;
154
155 *handle = (fstyp_mod_handle_t)h;
156 return (0);
157 }
158
159 void
fstyp_mod_fini(fstyp_mod_handle_t handle)160 fstyp_mod_fini(fstyp_mod_handle_t handle)
161 {
162 struct fstyp_pcfs *h = (struct fstyp_pcfs *)handle;
163
164 if (h->attr == NULL) {
165 nvlist_free(h->attr);
166 h->attr = NULL;
167 }
168 free(h);
169 }
170
171 int
fstyp_mod_ident(fstyp_mod_handle_t handle)172 fstyp_mod_ident(fstyp_mod_handle_t handle)
173 {
174 struct fstyp_pcfs *h = (struct fstyp_pcfs *)handle;
175
176 return (is_pcfs(h));
177 }
178
179 int
fstyp_mod_get_attr(fstyp_mod_handle_t handle,nvlist_t ** attrp)180 fstyp_mod_get_attr(fstyp_mod_handle_t handle, nvlist_t **attrp)
181 {
182 struct fstyp_pcfs *h = (struct fstyp_pcfs *)handle;
183 int error;
184
185 if (h->attr == NULL) {
186 if (nvlist_alloc(&h->attr, NV_UNIQUE_NAME_TYPE, 0)) {
187 return (FSTYP_ERR_NOMEM);
188 }
189 if ((error = get_attr(h)) != 0) {
190 nvlist_free(h->attr);
191 h->attr = NULL;
192 return (error);
193 }
194 }
195
196 *attrp = h->attr;
197 return (0);
198 }
199
200 int
fstyp_mod_dump(fstyp_mod_handle_t handle,FILE * fout,FILE * ferr)201 fstyp_mod_dump(fstyp_mod_handle_t handle, FILE *fout, FILE *ferr)
202 {
203 struct fstyp_pcfs *h = (struct fstyp_pcfs *)handle;
204
205 return (dumpfs(h, fout, ferr));
206 }
207
208
209 /*
210 * Read in boot sector. Convert into host endianness where possible.
211 */
212 static int
read_bootsec(fstyp_pcfs_t * h)213 read_bootsec(fstyp_pcfs_t *h)
214 {
215 struct dk_minfo dkminfo;
216 char *buf;
217 size_t size = PC_SECSIZE;
218
219 if (ioctl(h->fd, DKIOCGMEDIAINFO, &dkminfo) != -1) {
220 if (dkminfo.dki_lbsize != 0)
221 size = dkminfo.dki_lbsize;
222 }
223
224 buf = malloc(size);
225 if (buf == NULL)
226 return (FSTYP_ERR_NOMEM);
227
228 (void) lseek(h->fd, h->offset, SEEK_SET);
229 if (read(h->fd, buf, size) != (ssize_t)size) {
230 free(buf);
231 return (FSTYP_ERR_IO);
232 }
233
234 bcopy(buf, &h->bs, sizeof (h->bs));
235 bcopy(buf + sizeof (struct bootsec), &h->bs16, sizeof (h->bs16));
236 bcopy(buf + sizeof (struct bootsec), &h->bs32, sizeof (h->bs32));
237 free(buf);
238
239 h->bs.fatsec = ltohs(h->bs.fatsec);
240 h->bs.spt = ltohs(h->bs.spt);
241 h->bs.nhead = ltohs(h->bs.nhead);
242 h->bs.hiddensec = ltohi(h->bs.hiddensec);
243 h->bs.totalsec = ltohi(h->bs.totalsec);
244
245 h->bs32.f_fatlength = ltohi(h->bs32.f_fatlength);
246 h->bs32.f_flags = ltohs(h->bs32.f_flags);
247 h->bs32.f_rootcluster = ltohi(h->bs32.f_rootcluster);
248 h->bs32.f_infosector = ltohs(h->bs32.f_infosector);
249 h->bs32.f_backupboot = ltohs(h->bs32.f_backupboot);
250
251 h->bps = PC_BPSEC(h);
252
253 return (0);
254 }
255
256 static int
valid_media(fstyp_pcfs_t * h)257 valid_media(fstyp_pcfs_t *h)
258 {
259 switch (h->bs.mediadesriptor) {
260 case MD_FIXED:
261 case SS8SPT:
262 case DS8SPT:
263 case SS9SPT:
264 case DS9SPT:
265 case DS18SPT:
266 case DS9_15SPT:
267 return (1);
268 default:
269 return (0);
270 }
271 }
272
273 static int
well_formed(fstyp_pcfs_t * h)274 well_formed(fstyp_pcfs_t *h)
275 {
276 int fatmatch;
277
278 if (h->bs16.f_bootsig == 0x29) {
279 fatmatch = ((h->bs16.f_typestring[0] == 'F' &&
280 h->bs16.f_typestring[1] == 'A' &&
281 h->bs16.f_typestring[2] == 'T') &&
282 (h->bs.fatsec > 0) &&
283 ((PC_NSEC(h) == 0 && h->bs.totalsec > 0) ||
284 PC_NSEC(h) > 0));
285 } else if (h->bs32.f_bootsig == 0x29) {
286 fatmatch = ((h->bs32.f_typestring[0] == 'F' &&
287 h->bs32.f_typestring[1] == 'A' &&
288 h->bs32.f_typestring[2] == 'T') &&
289 (h->bs.fatsec == 0 && h->bs32.f_fatlength > 0) &&
290 ((PC_NSEC(h) == 0 && h->bs.totalsec > 0) ||
291 PC_NSEC(h) > 0));
292 } else {
293 fatmatch = (PC_NSEC(h) > 0 && h->bs.fatsec > 0);
294 }
295
296 return (fatmatch && h->bps > 0 && h->bps % 512 == 0 &&
297 h->bs.spcl > 0 && PC_RESSEC(h) >= 1 && h->bs.nfat > 0);
298 }
299
300 static void
calculate_parameters(fstyp_pcfs_t * h)301 calculate_parameters(fstyp_pcfs_t *h)
302 {
303 if (PC_NSEC(h) != 0) {
304 h->TotSec = PC_NSEC(h);
305 } else {
306 h->TotSec = h->bs.totalsec;
307 }
308 if (h->bs.fatsec != 0) {
309 h->FATSz = h->bs.fatsec;
310 } else {
311 h->FATSz = h->bs32.f_fatlength;
312 }
313 if ((h->bps == 0) || (h->bs.spcl == 0)) {
314 return;
315 }
316 h->RootDirSectors =
317 ((PC_NROOTENT(h) * 32) + (h->bps - 1)) / h->bps;
318 h->FirstDataSector =
319 PC_RESSEC(h) + h->bs.nfat * h->FATSz + h->RootDirSectors;
320 h->DataSec = h->TotSec - h->FirstDataSector;
321 h->CountOfClusters = h->DataSec / h->bs.spcl;
322 }
323
324 static void
determine_fattype(fstyp_pcfs_t * h)325 determine_fattype(fstyp_pcfs_t *h)
326 {
327 if (h->CountOfClusters == 0) {
328 h->fattype = 0;
329 return;
330 }
331
332 if (h->CountOfClusters < 4085) {
333 h->fattype = 12;
334 } else if (h->CountOfClusters < 65525) {
335 h->fattype = 16;
336 } else {
337 h->fattype = 32;
338 }
339 }
340
341 static void
get_label(fstyp_pcfs_t * h)342 get_label(fstyp_pcfs_t *h)
343 {
344 /*
345 * Use label from the boot sector by default.
346 * Can overwrite later with the one from root directory.
347 */
348 (void) memcpy(h->volume_label, PC_LABEL_ADDR(h), PC_LABEL_SIZE);
349 h->volume_label[PC_LABEL_SIZE] = '\0';
350
351 if (h->fattype == 0) {
352 return;
353 } else if (FSTYP_IS_32(h)) {
354 get_label_32(h);
355 } else {
356 get_label_16(h);
357 }
358 }
359
360 /*
361 * Get volume label from the root directory entry.
362 * In FAT12/16 the root directory is of fixed size.
363 * It immediately follows the FATs
364 */
365 static void
get_label_16(fstyp_pcfs_t * h)366 get_label_16(fstyp_pcfs_t *h)
367 {
368 ulong_t FirstRootDirSecNum;
369 int secsize;
370 off_t offset;
371 uint8_t buf[PC_SECSIZE * 4];
372 int i;
373 int nent, resid;
374
375 if ((secsize = h->bps) > sizeof (buf)) {
376 return;
377 }
378
379 FirstRootDirSecNum = PC_RESSEC(h) + h->bs.nfat * h->bs.fatsec;
380 offset = h->offset + FirstRootDirSecNum * secsize;
381 resid = PC_NROOTENT(h);
382
383 for (i = 0; i < h->RootDirSectors; i++) {
384 (void) lseek(h->fd, offset, SEEK_SET);
385 if (read(h->fd, buf, secsize) != secsize) {
386 return;
387 }
388
389 nent = secsize / sizeof (struct pcdir);
390 if (nent > resid) {
391 nent = resid;
392 }
393 if (dir_find_label(h, (struct pcdir *)buf, nent)) {
394 return;
395 }
396
397 resid -= nent;
398 offset += PC_SECSIZE;
399 }
400 }
401
402 /*
403 * Get volume label from the root directory entry.
404 * In FAT32 root is a usual directory, a cluster chain.
405 * It starts at BPB_RootClus.
406 */
407 static void
get_label_32(fstyp_pcfs_t * h)408 get_label_32(fstyp_pcfs_t *h)
409 {
410 off_t offset;
411 int clustersize;
412 int n;
413 ulong_t FirstSectorofCluster;
414 uint8_t *buf;
415 int nent;
416 int cnt = 0;
417
418 clustersize = h->bs.spcl * h->bps;
419 if ((clustersize == 0) || (clustersize > FSTYP_MAX_CLUSTER_SIZE) ||
420 ((buf = calloc(1, clustersize)) == NULL)) {
421 return;
422 }
423
424 for (n = h->bs32.f_rootcluster; n != 0; n = next_cluster_32(h, n)) {
425 FirstSectorofCluster =
426 (n - 2) * h->bs.spcl + h->FirstDataSector;
427 offset = h->offset + FirstSectorofCluster * h->bps;
428 (void) lseek(h->fd, offset, SEEK_SET);
429 if (read(h->fd, buf, clustersize) != clustersize) {
430 break;
431 }
432
433 nent = clustersize / sizeof (struct pcdir);
434 if (dir_find_label(h, (struct pcdir *)buf, nent)) {
435 break;
436 }
437
438 if (++cnt > FSTYP_MAX_DIR_SIZE / clustersize) {
439 break;
440 }
441 }
442
443 free(buf);
444 }
445
446 /*
447 * Get a FAT entry pointing to the next file cluster
448 */
449 int
next_cluster_32(fstyp_pcfs_t * h,int n)450 next_cluster_32(fstyp_pcfs_t *h, int n)
451 {
452 uint8_t buf[PC_SECSIZE];
453 ulong_t ThisFATSecNum;
454 ulong_t ThisFATEntOffset;
455 off_t offset;
456 uint32_t val;
457 int next = 0;
458
459 ThisFATSecNum = PC_RESSEC(h) + (n * 4) / h->bps;
460 ThisFATEntOffset = (n * 4) % h->bps;
461 offset = h->offset + ThisFATSecNum * h->bps;
462
463 (void) lseek(h->fd, offset, SEEK_SET);
464 if (read(h->fd, buf, sizeof (buf)) == sizeof (buf)) {
465 val = buf[ThisFATEntOffset] & 0x0fffffff;
466 next = ltohi(val);
467 }
468
469 return (next);
470 }
471
472 /*
473 * Given an array of pcdir structs, find one containing volume label.
474 */
475 static boolean_t
dir_find_label(fstyp_pcfs_t * h,struct pcdir * d,int nent)476 dir_find_label(fstyp_pcfs_t *h, struct pcdir *d, int nent)
477 {
478 int i;
479
480 for (i = 0; i < nent; i++, d++) {
481 if (PCDL_IS_LFN(d))
482 continue;
483 if ((d->pcd_filename[0] != PCD_UNUSED) &&
484 (d->pcd_filename[0] != PCD_ERASED) &&
485 ((d->pcd_attr & (PCA_LABEL | PCA_DIR)) == PCA_LABEL) &&
486 (d->un.pcd_scluster_hi == 0) &&
487 (d->pcd_scluster_lo == 0)) {
488 (void) memcpy(h->volume_label, d->pcd_filename,
489 PC_LABEL_SIZE);
490 h->volume_label[PC_LABEL_SIZE] = '\0';
491 return (B_TRUE);
492 }
493 }
494 return (B_FALSE);
495 }
496
497 static int
is_pcfs(fstyp_pcfs_t * h)498 is_pcfs(fstyp_pcfs_t *h)
499 {
500 int error;
501
502 if ((error = read_bootsec(h)) != 0) {
503 return (error);
504 }
505 if (!valid_media(h)) {
506 return (FSTYP_ERR_NO_MATCH);
507 }
508 if (!well_formed(h)) {
509 return (FSTYP_ERR_NO_MATCH);
510 }
511
512 calculate_parameters(h);
513 determine_fattype(h);
514 get_label(h);
515
516 return (0);
517 }
518
519 static int
dumpfs(fstyp_pcfs_t * h,FILE * fout,FILE * ferr __unused)520 dumpfs(fstyp_pcfs_t *h, FILE *fout, FILE *ferr __unused)
521 {
522 /*
523 * If fat type was not detected, then the other data is
524 * likely bogus.
525 */
526 if (h->fattype == 0)
527 return (FSTYP_ERR_NO_MATCH);
528
529 (void) fprintf(fout, "Filesystem type: FAT%d\n", h->fattype);
530 (void) fprintf(fout,
531 "Bytes Per Sector %d\t\tSectors Per Cluster %d\n",
532 h->bps, h->bs.spcl);
533 (void) fprintf(fout,
534 "Reserved Sectors %d\t\tNumber of FATs %d\n",
535 (unsigned short)PC_RESSEC(h), h->bs.nfat);
536 (void) fprintf(fout,
537 "Root Dir Entries %d\t\tNumber of Sectors %d\n",
538 (unsigned short)PC_NROOTENT(h), h->TotSec);
539 (void) fprintf(fout,
540 "Sectors Per FAT %d\t\tSectors Per Track %d\n",
541 h->FATSz, h->bs.spt);
542 (void) fprintf(fout,
543 "Number of Heads %d\t\tNumber Hidden Sectors %d\n",
544 h->bs.nhead, h->bs.hiddensec);
545 (void) fprintf(fout, "Volume ID: 0x%x\n", PC_VOLID(h));
546 (void) fprintf(fout, "Volume Label: %s\n", h->volume_label);
547 (void) fprintf(fout, "Drive Number: 0x%x\n", PC_DRVNUM(h));
548 (void) fprintf(fout, "Media Type: 0x%x ", h->bs.mediadesriptor);
549
550 switch (h->bs.mediadesriptor) {
551 case MD_FIXED:
552 (void) fprintf(fout, "\"Fixed\" Disk\n");
553 break;
554 case SS8SPT:
555 (void) fprintf(fout, "Single Sided, 8 Sectors Per Track\n");
556 break;
557 case DS8SPT:
558 (void) fprintf(fout, "Double Sided, 8 Sectors Per Track\n");
559 break;
560 case SS9SPT:
561 (void) fprintf(fout, "Single Sided, 9 Sectors Per Track\n");
562 break;
563 case DS9SPT:
564 (void) fprintf(fout, "Double Sided, 9 Sectors Per Track\n");
565 break;
566 case DS18SPT:
567 (void) fprintf(fout, "Double Sided, 18 Sectors Per Track\n");
568 break;
569 case DS9_15SPT:
570 (void) fprintf(fout, "Double Sided, 9-15 Sectors Per Track\n");
571 break;
572 default:
573 (void) fprintf(fout, "Unknown Media Type\n");
574 }
575
576 return (0);
577 }
578
579 #define ADD_STRING(h, name, value) \
580 if (nvlist_add_string(h->attr, name, value) != 0) { \
581 return (FSTYP_ERR_NOMEM); \
582 }
583
584 #define ADD_UINT32(h, name, value) \
585 if (nvlist_add_uint32(h->attr, name, value) != 0) { \
586 return (FSTYP_ERR_NOMEM); \
587 }
588
589 #define ADD_UINT64(h, name, value) \
590 if (nvlist_add_uint64(h->attr, name, value) != 0) { \
591 return (FSTYP_ERR_NOMEM); \
592 }
593
594 #define ADD_BOOL(h, name, value) \
595 if (nvlist_add_boolean_value(h->attr, name, value) != 0) { \
596 return (FSTYP_ERR_NOMEM); \
597 }
598
599 static int
get_attr(fstyp_pcfs_t * h)600 get_attr(fstyp_pcfs_t *h)
601 {
602 char s[64];
603
604 ADD_UINT32(h, "bytes_per_sector", h->bps);
605 ADD_UINT32(h, "sectors_per_cluster", h->bs.spcl);
606 ADD_UINT32(h, "reserved_sectors", PC_RESSEC(h));
607 ADD_UINT32(h, "fats", h->bs.nfat);
608 ADD_UINT32(h, "root_entry_count", PC_NROOTENT(h));
609 ADD_UINT32(h, "total_sectors_16", PC_NSEC(h));
610 ADD_UINT32(h, "media", h->bs.mediadesriptor);
611 ADD_UINT32(h, "fat_size_16", h->bs.fatsec);
612 ADD_UINT32(h, "sectors_per_track", h->bs.spt);
613 ADD_UINT32(h, "heads", h->bs.nhead);
614 ADD_UINT32(h, "hidden_sectors", h->bs.hiddensec);
615 ADD_UINT32(h, "total_sectors_32", h->bs.totalsec);
616 ADD_UINT32(h, "drive_number", PC_DRVNUM(h));
617 ADD_UINT32(h, "volume_id", PC_VOLID(h));
618 ADD_STRING(h, "volume_label", h->volume_label);
619 if (FSTYP_IS_32(h)) {
620 ADD_UINT32(h, "fat_size_32", h->bs32.f_fatlength);
621 }
622 ADD_UINT32(h, "total_sectors", h->TotSec);
623 ADD_UINT32(h, "fat_size", h->FATSz);
624 ADD_UINT32(h, "count_of_clusters", h->CountOfClusters);
625 ADD_UINT32(h, "fat_entry_size", h->fattype);
626
627 ADD_BOOL(h, "gen_clean", B_TRUE);
628 if (PC_VOLID(a) != 0) {
629 (void) snprintf(s, sizeof (s), "%08x", PC_VOLID(a));
630 ADD_STRING(h, "gen_guid", s);
631 }
632 (void) snprintf(s, sizeof (s), "%d", h->fattype);
633 ADD_STRING(h, "gen_version", s);
634 ADD_STRING(h, "gen_volume_label", h->volume_label);
635
636 return (0);
637 }
638