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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
30 * Portions of this source code were derived from Berkeley 4.3 BSD
31 * under license from the Regents of the University of California.
34 #pragma ident "%Z%%M% %I% %E% SMI"
37 * libfstyp module for pcfs
39 #include <sys/param.h>
40 #include <sys/types.h>
41 #include <sys/mntent.h>
43 #include <sys/fs/pc_fs.h>
44 #include <sys/fs/pc_label.h>
45 #include <sys/fs/pc_dir.h>
52 #include <sys/mnttab.h>
54 #include <libfstyp_module.h>
56 #define PC_LABEL_SIZE 11
58 /* for the <sys/fs/pc_dir.h> PCDL_IS_LFN macro */
59 int enable_long_filenames
= 1;
64 uchar_t bps
[2]; /* bytes per sector */
65 uchar_t spcl
; /* sectors per allocation unit */
66 uchar_t res_sec
[2]; /* reserved sectors, starting at 0 */
67 uchar_t nfat
; /* number of FATs */
68 uchar_t rdirents
[2]; /* number of root directory entries */
69 uchar_t numsect
[2]; /* old total sectors in logical image */
70 uchar_t mediadesriptor
; /* media descriptor byte */
71 ushort_t fatsec
; /* number of sectors per FAT */
72 ushort_t spt
; /* sectors per track */
73 ushort_t nhead
; /* number of heads */
74 uint_t hiddensec
; /* number of hidden sectors */
75 uint_t totalsec
; /* total sectors in logical image */
78 struct fstyp_fat16_bs
{
84 uint8_t f_typestring
[8];
87 struct fstyp_fat32_bs
{
92 uint32_t f_rootcluster
;
93 uint16_t f_infosector
;
94 uint16_t f_backupboot
;
95 uint8_t f_reserved2
[12];
101 uint8_t f_typestring
[8];
104 typedef struct fstyp_pcfs
{
109 struct fstyp_fat16_bs bs16
;
110 struct fstyp_fat32_bs bs32
;
113 char volume_label
[PC_LABEL_SIZE
+ 1];
115 /* parameters derived or calculated per FAT spec */
118 ulong_t RootDirSectors
;
119 ulong_t FirstDataSector
;
121 ulong_t CountOfClusters
;
125 /* We should eventually make the structs "packed" so these won't be needed */
126 #define PC_BPSEC(h) ltohs((h)->bs.bps[0])
127 #define PC_RESSEC(h) ltohs((h)->bs.res_sec[0])
128 #define PC_NROOTENT(h) ltohs((h)->bs.rdirents[0])
129 #define PC_NSEC(h) ltohs((h)->bs.numsect[0])
130 #define PC_DRVNUM(h) (FSTYP_IS_32(h) ? (h)->bs32.f_drvnum : \
132 #define PC_VOLID(a) (FSTYP_IS_32(h) ? ltohi((h)->bs32.f_volid[0]) : \
133 ltohi((h)->bs16.f_volid[0]))
134 #define PC_LABEL_ADDR(a) (FSTYP_IS_32(h) ? \
135 &((h)->bs32.f_label[0]) : &((h)->bs16.f_label[0]))
137 #define FSTYP_IS_32(h) ((h)->fattype == 32)
139 #define FSTYP_MAX_CLUSTER_SIZE (64 * 1024) /* though officially 32K */
140 #define FSTYP_MAX_DIR_SIZE (65536 * 32)
142 static int read_bootsec(fstyp_pcfs_t
*h
);
143 static int valid_media(fstyp_pcfs_t
*h
);
144 static int well_formed(fstyp_pcfs_t
*h
);
145 static void calculate_parameters(fstyp_pcfs_t
*h
);
146 static void determine_fattype(fstyp_pcfs_t
*h
);
147 static void get_label(fstyp_pcfs_t
*h
);
148 static void get_label_16(fstyp_pcfs_t
*h
);
149 static void get_label_32(fstyp_pcfs_t
*h
);
150 static int next_cluster_32(fstyp_pcfs_t
*h
, int n
);
151 static boolean_t
dir_find_label(fstyp_pcfs_t
*h
, struct pcdir
*d
, int nent
);
152 static int is_pcfs(fstyp_pcfs_t
*h
);
153 static int dumpfs(fstyp_pcfs_t
*h
, FILE *fout
, FILE *ferr
);
154 static int get_attr(fstyp_pcfs_t
*h
);
156 int fstyp_mod_init(int fd
, off_t offset
, fstyp_mod_handle_t
*handle
);
157 void fstyp_mod_fini(fstyp_mod_handle_t handle
);
158 int fstyp_mod_ident(fstyp_mod_handle_t handle
);
159 int fstyp_mod_get_attr(fstyp_mod_handle_t handle
, nvlist_t
**attrp
);
160 int fstyp_mod_dump(fstyp_mod_handle_t handle
, FILE *fout
, FILE *ferr
);
163 fstyp_mod_init(int fd
, off_t offset
, fstyp_mod_handle_t
*handle
)
165 struct fstyp_pcfs
*h
;
167 if ((h
= calloc(1, sizeof (struct fstyp_pcfs
))) == NULL
) {
168 return (FSTYP_ERR_NOMEM
);
173 *handle
= (fstyp_mod_handle_t
)h
;
178 fstyp_mod_fini(fstyp_mod_handle_t handle
)
180 struct fstyp_pcfs
*h
= (struct fstyp_pcfs
*)handle
;
182 if (h
->attr
== NULL
) {
183 nvlist_free(h
->attr
);
190 fstyp_mod_ident(fstyp_mod_handle_t handle
)
192 struct fstyp_pcfs
*h
= (struct fstyp_pcfs
*)handle
;
198 fstyp_mod_get_attr(fstyp_mod_handle_t handle
, nvlist_t
**attrp
)
200 struct fstyp_pcfs
*h
= (struct fstyp_pcfs
*)handle
;
203 if (h
->attr
== NULL
) {
204 if (nvlist_alloc(&h
->attr
, NV_UNIQUE_NAME_TYPE
, 0)) {
205 return (FSTYP_ERR_NOMEM
);
207 if ((error
= get_attr(h
)) != 0) {
208 nvlist_free(h
->attr
);
219 fstyp_mod_dump(fstyp_mod_handle_t handle
, FILE *fout
, FILE *ferr
)
221 struct fstyp_pcfs
*h
= (struct fstyp_pcfs
*)handle
;
223 return (dumpfs(h
, fout
, ferr
));
228 * Read in boot sector. Convert into host endianness where possible.
231 read_bootsec(fstyp_pcfs_t
*h
)
233 char buf
[PC_SECSIZE
];
235 (void) lseek(h
->fd
, h
->offset
, SEEK_SET
);
236 if (read(h
->fd
, buf
, sizeof (buf
)) != sizeof (buf
)) {
237 return (FSTYP_ERR_IO
);
240 bcopy(buf
, &h
->bs
, sizeof (h
->bs
));
241 bcopy(buf
+ sizeof (struct bootsec
), &h
->bs16
, sizeof (h
->bs16
));
242 bcopy(buf
+ sizeof (struct bootsec
), &h
->bs32
, sizeof (h
->bs32
));
244 h
->bs
.fatsec
= ltohs(h
->bs
.fatsec
);
245 h
->bs
.spt
= ltohs(h
->bs
.spt
);
246 h
->bs
.nhead
= ltohs(h
->bs
.nhead
);
247 h
->bs
.hiddensec
= ltohi(h
->bs
.hiddensec
);
248 h
->bs
.totalsec
= ltohi(h
->bs
.totalsec
);
250 h
->bs32
.f_fatlength
= ltohi(h
->bs32
.f_fatlength
);
251 h
->bs32
.f_flags
= ltohs(h
->bs32
.f_flags
);
252 h
->bs32
.f_rootcluster
= ltohi(h
->bs32
.f_rootcluster
);
253 h
->bs32
.f_infosector
= ltohs(h
->bs32
.f_infosector
);
254 h
->bs32
.f_backupboot
= ltohs(h
->bs32
.f_backupboot
);
256 h
->bps
= PC_BPSEC(h
);
262 valid_media(fstyp_pcfs_t
*h
)
264 switch (h
->bs
.mediadesriptor
) {
279 well_formed(fstyp_pcfs_t
*h
)
283 if (h
->bs16
.f_bootsig
== 0x29) {
284 fatmatch
= ((h
->bs16
.f_typestring
[0] == 'F' &&
285 h
->bs16
.f_typestring
[1] == 'A' &&
286 h
->bs16
.f_typestring
[2] == 'T') &&
287 (h
->bs
.fatsec
> 0) &&
288 ((PC_NSEC(h
) == 0 && h
->bs
.totalsec
> 0) ||
290 } else if (h
->bs32
.f_bootsig
== 0x29) {
291 fatmatch
= ((h
->bs32
.f_typestring
[0] == 'F' &&
292 h
->bs32
.f_typestring
[1] == 'A' &&
293 h
->bs32
.f_typestring
[2] == 'T') &&
294 (h
->bs
.fatsec
== 0 && h
->bs32
.f_fatlength
> 0) &&
295 ((PC_NSEC(h
) == 0 && h
->bs
.totalsec
> 0) ||
298 fatmatch
= (PC_NSEC(h
) > 0 && h
->bs
.fatsec
> 0);
301 return (fatmatch
&& h
->bps
> 0 && h
->bps
% 512 == 0 &&
302 h
->bs
.spcl
> 0 && PC_RESSEC(h
) >= 1 && h
->bs
.nfat
> 0);
306 calculate_parameters(fstyp_pcfs_t
*h
)
308 if (PC_NSEC(h
) != 0) {
309 h
->TotSec
= PC_NSEC(h
);
311 h
->TotSec
= h
->bs
.totalsec
;
313 if (h
->bs
.fatsec
!= 0) {
314 h
->FATSz
= h
->bs
.fatsec
;
316 h
->FATSz
= h
->bs32
.f_fatlength
;
318 if ((h
->bps
== 0) || (h
->bs
.spcl
== 0)) {
322 ((PC_NROOTENT(h
) * 32) + (h
->bps
- 1)) / h
->bps
;
324 PC_RESSEC(h
) + h
->bs
.nfat
* h
->FATSz
+ h
->RootDirSectors
;
325 h
->DataSec
= h
->TotSec
- h
->FirstDataSector
;
326 h
->CountOfClusters
= h
->DataSec
/ h
->bs
.spcl
;
330 determine_fattype(fstyp_pcfs_t
*h
)
332 if ((h
->CountOfClusters
>= 4085 && h
->CountOfClusters
<= 4095) ||
333 (h
->CountOfClusters
>= 65525 && h
->CountOfClusters
<= 65535)) {
335 } else if (h
->CountOfClusters
< 4085) {
337 } else if (h
->CountOfClusters
< 65525) {
345 get_label(fstyp_pcfs_t
*h
)
348 * Use label from the boot sector by default.
349 * Can overwrite later with the one from root directory.
351 (void) memcpy(h
->volume_label
, PC_LABEL_ADDR(h
), PC_LABEL_SIZE
);
352 h
->volume_label
[PC_LABEL_SIZE
] = '\0';
354 if (h
->fattype
== 0) {
356 } else if (FSTYP_IS_32(h
)) {
364 * Get volume label from the root directory entry.
365 * In FAT12/16 the root directory is of fixed size.
366 * It immediately follows the FATs
369 get_label_16(fstyp_pcfs_t
*h
)
371 ulong_t FirstRootDirSecNum
;
374 uint8_t buf
[PC_SECSIZE
* 4];
378 if ((secsize
= h
->bps
) > sizeof (buf
)) {
382 FirstRootDirSecNum
= PC_RESSEC(h
) + h
->bs
.nfat
* h
->bs
.fatsec
;
383 offset
= h
->offset
+ FirstRootDirSecNum
* secsize
;
384 resid
= PC_NROOTENT(h
);
386 for (i
= 0; i
< h
->RootDirSectors
; i
++) {
387 (void) lseek(h
->fd
, offset
, SEEK_SET
);
388 if (read(h
->fd
, buf
, secsize
) != secsize
) {
392 nent
= secsize
/ sizeof (struct pcdir
);
396 if (dir_find_label(h
, (struct pcdir
*)buf
, nent
)) {
401 offset
+= PC_SECSIZE
;
406 * Get volume label from the root directory entry.
407 * In FAT32 root is a usual directory, a cluster chain.
408 * It starts at BPB_RootClus.
411 get_label_32(fstyp_pcfs_t
*h
)
416 ulong_t FirstSectorofCluster
;
421 clustersize
= h
->bs
.spcl
* h
->bps
;
422 if ((clustersize
== 0) || (clustersize
> FSTYP_MAX_CLUSTER_SIZE
) ||
423 ((buf
= calloc(1, clustersize
)) == NULL
)) {
427 for (n
= h
->bs32
.f_rootcluster
; n
!= 0; n
= next_cluster_32(h
, n
)) {
428 FirstSectorofCluster
=
429 (n
- 2) * h
->bs
.spcl
+ h
->FirstDataSector
;
430 offset
= h
->offset
+ FirstSectorofCluster
* h
->bps
;
431 (void) lseek(h
->fd
, offset
, SEEK_SET
);
432 if (read(h
->fd
, buf
, clustersize
) != clustersize
) {
436 nent
= clustersize
/ sizeof (struct pcdir
);
437 if (dir_find_label(h
, (struct pcdir
*)buf
, nent
)) {
441 if (++cnt
> FSTYP_MAX_DIR_SIZE
/ clustersize
) {
450 * Get a FAT entry pointing to the next file cluster
453 next_cluster_32(fstyp_pcfs_t
*h
, int n
)
455 uint8_t buf
[PC_SECSIZE
];
456 ulong_t ThisFATSecNum
;
457 ulong_t ThisFATEntOffset
;
462 ThisFATSecNum
= PC_RESSEC(h
) + (n
* 4) / h
->bps
;
463 ThisFATEntOffset
= (n
* 4) % h
->bps
;
464 offset
= h
->offset
+ ThisFATSecNum
* h
->bps
;
466 (void) lseek(h
->fd
, offset
, SEEK_SET
);
467 if (read(h
->fd
, buf
, sizeof (buf
)) == sizeof (buf
)) {
468 val
= buf
[ThisFATEntOffset
] & 0x0fffffff;
476 * Given an array of pcdir structs, find one containing volume label.
479 dir_find_label(fstyp_pcfs_t
*h
, struct pcdir
*d
, int nent
)
483 for (i
= 0; i
< nent
; i
++, d
++) {
486 if ((d
->pcd_filename
[0] != PCD_UNUSED
) &&
487 (d
->pcd_filename
[0] != PCD_ERASED
) &&
488 ((d
->pcd_attr
& (PCA_LABEL
| PCA_DIR
)) == PCA_LABEL
) &&
489 (d
->un
.pcd_scluster_hi
== 0) &&
490 (d
->pcd_scluster_lo
== 0)) {
491 (void) memcpy(h
->volume_label
, d
->pcd_filename
,
493 h
->volume_label
[PC_LABEL_SIZE
] = '\0';
501 is_pcfs(fstyp_pcfs_t
*h
)
505 if ((error
= read_bootsec(h
)) != 0) {
508 if (!valid_media(h
)) {
509 return (FSTYP_ERR_NO_MATCH
);
511 if (!well_formed(h
)) {
512 return (FSTYP_ERR_NO_MATCH
);
515 calculate_parameters(h
);
516 determine_fattype(h
);
524 dumpfs(fstyp_pcfs_t
*h
, FILE *fout
, FILE *ferr
)
527 "Bytes Per Sector %d\t\tSectors Per Cluster %d\n",
530 "Reserved Sectors %d\t\tNumber of FATs %d\n",
531 (unsigned short)PC_RESSEC(h
), h
->bs
.nfat
);
533 "Root Dir Entries %d\t\tNumber of Sectors %d\n",
534 (unsigned short)PC_NROOTENT(h
), (unsigned short)PC_NSEC(h
));
536 "Sectors Per FAT %d\t\tSectors Per Track %d\n",
537 h
->bs
.fatsec
, h
->bs
.spt
);
539 "Number of Heads %d\t\tNumber Hidden Sectors %d\n",
540 h
->bs
.nhead
, h
->bs
.hiddensec
);
541 (void) fprintf(fout
, "Volume ID: 0x%x\n", PC_VOLID(h
));
542 (void) fprintf(fout
, "Volume Label: %s\n", h
->volume_label
);
543 (void) fprintf(fout
, "Drive Number: 0x%x\n", PC_DRVNUM(h
));
544 (void) fprintf(fout
, "Media Type: 0x%x ", h
->bs
.mediadesriptor
);
546 switch (h
->bs
.mediadesriptor
) {
548 (void) fprintf(fout
, "\"Fixed\" Disk\n");
551 (void) fprintf(fout
, "Single Sided, 8 Sectors Per Track\n");
554 (void) fprintf(fout
, "Double Sided, 8 Sectors Per Track\n");
557 (void) fprintf(fout
, "Single Sided, 9 Sectors Per Track\n");
560 (void) fprintf(fout
, "Double Sided, 9 Sectors Per Track\n");
563 (void) fprintf(fout
, "Double Sided, 18 Sectors Per Track\n");
566 (void) fprintf(fout
, "Double Sided, 9-15 Sectors Per Track\n");
569 (void) fprintf(fout
, "Unknown Media Type\n");
575 #define ADD_STRING(h, name, value) \
576 if (nvlist_add_string(h->attr, name, value) != 0) { \
577 return (FSTYP_ERR_NOMEM); \
580 #define ADD_UINT32(h, name, value) \
581 if (nvlist_add_uint32(h->attr, name, value) != 0) { \
582 return (FSTYP_ERR_NOMEM); \
585 #define ADD_UINT64(h, name, value) \
586 if (nvlist_add_uint64(h->attr, name, value) != 0) { \
587 return (FSTYP_ERR_NOMEM); \
590 #define ADD_BOOL(h, name, value) \
591 if (nvlist_add_boolean_value(h->attr, name, value) != 0) { \
592 return (FSTYP_ERR_NOMEM); \
596 get_attr(fstyp_pcfs_t
*h
)
600 ADD_UINT32(h
, "bytes_per_sector", h
->bps
);
601 ADD_UINT32(h
, "sectors_per_cluster", h
->bs
.spcl
);
602 ADD_UINT32(h
, "reserved_sectors", PC_RESSEC(h
));
603 ADD_UINT32(h
, "fats", h
->bs
.nfat
);
604 ADD_UINT32(h
, "root_entry_count", PC_NROOTENT(h
));
605 ADD_UINT32(h
, "total_sectors_16", PC_NSEC(h
));
606 ADD_UINT32(h
, "media", h
->bs
.mediadesriptor
);
607 ADD_UINT32(h
, "fat_size_16", h
->bs
.fatsec
);
608 ADD_UINT32(h
, "sectors_per_track", h
->bs
.spt
);
609 ADD_UINT32(h
, "heads", h
->bs
.nhead
);
610 ADD_UINT32(h
, "hidden_sectors", h
->bs
.hiddensec
);
611 ADD_UINT32(h
, "total_sectors_32", h
->bs
.totalsec
);
612 ADD_UINT32(h
, "drive_number", PC_DRVNUM(h
));
613 ADD_UINT32(h
, "volume_id", PC_VOLID(h
));
614 ADD_STRING(h
, "volume_label", h
->volume_label
);
615 if (FSTYP_IS_32(h
)) {
616 ADD_UINT32(h
, "fat_size_32", h
->bs32
.f_fatlength
);
618 ADD_UINT32(h
, "total_sectors", h
->TotSec
);
619 ADD_UINT32(h
, "fat_size", h
->FATSz
);
620 ADD_UINT32(h
, "count_of_clusters", h
->CountOfClusters
);
621 ADD_UINT32(h
, "fat_entry_size", h
->fattype
);
623 ADD_BOOL(h
, "gen_clean", B_TRUE
);
624 if (PC_VOLID(a
) != 0) {
625 (void) snprintf(s
, sizeof (s
), "%08x", PC_VOLID(a
));
626 ADD_STRING(h
, "gen_guid", s
);
628 (void) snprintf(s
, sizeof (s
), "%d", h
->fattype
);
629 ADD_STRING(h
, "gen_version", s
);
630 ADD_STRING(h
, "gen_volume_label", h
->volume_label
);