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;
61 struct fstyp_fat16_bs
{
67 uint8_t f_typestring
[8];
70 struct fstyp_fat32_bs
{
75 uint32_t f_rootcluster
;
76 uint16_t f_infosector
;
77 uint16_t f_backupboot
;
78 uint8_t f_reserved2
[12];
84 uint8_t f_typestring
[8];
87 typedef struct fstyp_pcfs
{
92 struct fstyp_fat16_bs bs16
;
93 struct fstyp_fat32_bs bs32
;
96 char volume_label
[PC_LABEL_SIZE
+ 1];
98 /* parameters derived or calculated per FAT spec */
101 ulong_t RootDirSectors
;
102 ulong_t FirstDataSector
;
104 ulong_t CountOfClusters
;
108 /* We should eventually make the structs "packed" so these won't be needed */
109 #define PC_BPSEC(h) ltohs((h)->bs.bps[0])
110 #define PC_RESSEC(h) ltohs((h)->bs.res_sec[0])
111 #define PC_NROOTENT(h) ltohs((h)->bs.rdirents[0])
112 #define PC_NSEC(h) ltohs((h)->bs.numsect[0])
113 #define PC_DRVNUM(h) (FSTYP_IS_32(h) ? (h)->bs32.f_drvnum : \
115 #define PC_VOLID(a) (FSTYP_IS_32(h) ? ltohi((h)->bs32.f_volid[0]) : \
116 ltohi((h)->bs16.f_volid[0]))
117 #define PC_LABEL_ADDR(a) (FSTYP_IS_32(h) ? \
118 &((h)->bs32.f_label[0]) : &((h)->bs16.f_label[0]))
120 #define FSTYP_IS_32(h) ((h)->fattype == 32)
122 #define FSTYP_MAX_CLUSTER_SIZE (64 * 1024) /* though officially 32K */
123 #define FSTYP_MAX_DIR_SIZE (65536 * 32)
125 static int read_bootsec(fstyp_pcfs_t
*h
);
126 static int valid_media(fstyp_pcfs_t
*h
);
127 static int well_formed(fstyp_pcfs_t
*h
);
128 static void calculate_parameters(fstyp_pcfs_t
*h
);
129 static void determine_fattype(fstyp_pcfs_t
*h
);
130 static void get_label(fstyp_pcfs_t
*h
);
131 static void get_label_16(fstyp_pcfs_t
*h
);
132 static void get_label_32(fstyp_pcfs_t
*h
);
133 static int next_cluster_32(fstyp_pcfs_t
*h
, int n
);
134 static boolean_t
dir_find_label(fstyp_pcfs_t
*h
, struct pcdir
*d
, int nent
);
135 static int is_pcfs(fstyp_pcfs_t
*h
);
136 static int dumpfs(fstyp_pcfs_t
*h
, FILE *fout
, FILE *ferr
);
137 static int get_attr(fstyp_pcfs_t
*h
);
139 int fstyp_mod_init(int fd
, off_t offset
, fstyp_mod_handle_t
*handle
);
140 void fstyp_mod_fini(fstyp_mod_handle_t handle
);
141 int fstyp_mod_ident(fstyp_mod_handle_t handle
);
142 int fstyp_mod_get_attr(fstyp_mod_handle_t handle
, nvlist_t
**attrp
);
143 int fstyp_mod_dump(fstyp_mod_handle_t handle
, FILE *fout
, FILE *ferr
);
146 fstyp_mod_init(int fd
, off_t offset
, fstyp_mod_handle_t
*handle
)
148 struct fstyp_pcfs
*h
;
150 if ((h
= calloc(1, sizeof (struct fstyp_pcfs
))) == NULL
) {
151 return (FSTYP_ERR_NOMEM
);
156 *handle
= (fstyp_mod_handle_t
)h
;
161 fstyp_mod_fini(fstyp_mod_handle_t handle
)
163 struct fstyp_pcfs
*h
= (struct fstyp_pcfs
*)handle
;
165 if (h
->attr
== NULL
) {
166 nvlist_free(h
->attr
);
173 fstyp_mod_ident(fstyp_mod_handle_t handle
)
175 struct fstyp_pcfs
*h
= (struct fstyp_pcfs
*)handle
;
181 fstyp_mod_get_attr(fstyp_mod_handle_t handle
, nvlist_t
**attrp
)
183 struct fstyp_pcfs
*h
= (struct fstyp_pcfs
*)handle
;
186 if (h
->attr
== NULL
) {
187 if (nvlist_alloc(&h
->attr
, NV_UNIQUE_NAME_TYPE
, 0)) {
188 return (FSTYP_ERR_NOMEM
);
190 if ((error
= get_attr(h
)) != 0) {
191 nvlist_free(h
->attr
);
202 fstyp_mod_dump(fstyp_mod_handle_t handle
, FILE *fout
, FILE *ferr
)
204 struct fstyp_pcfs
*h
= (struct fstyp_pcfs
*)handle
;
206 return (dumpfs(h
, fout
, ferr
));
211 * Read in boot sector. Convert into host endianness where possible.
214 read_bootsec(fstyp_pcfs_t
*h
)
216 char buf
[PC_SECSIZE
];
218 (void) lseek(h
->fd
, h
->offset
, SEEK_SET
);
219 if (read(h
->fd
, buf
, sizeof (buf
)) != sizeof (buf
)) {
220 return (FSTYP_ERR_IO
);
223 bcopy(buf
, &h
->bs
, sizeof (h
->bs
));
224 bcopy(buf
+ sizeof (struct bootsec
), &h
->bs16
, sizeof (h
->bs16
));
225 bcopy(buf
+ sizeof (struct bootsec
), &h
->bs32
, sizeof (h
->bs32
));
227 h
->bs
.fatsec
= ltohs(h
->bs
.fatsec
);
228 h
->bs
.spt
= ltohs(h
->bs
.spt
);
229 h
->bs
.nhead
= ltohs(h
->bs
.nhead
);
230 h
->bs
.hiddensec
= ltohi(h
->bs
.hiddensec
);
231 h
->bs
.totalsec
= ltohi(h
->bs
.totalsec
);
233 h
->bs32
.f_fatlength
= ltohi(h
->bs32
.f_fatlength
);
234 h
->bs32
.f_flags
= ltohs(h
->bs32
.f_flags
);
235 h
->bs32
.f_rootcluster
= ltohi(h
->bs32
.f_rootcluster
);
236 h
->bs32
.f_infosector
= ltohs(h
->bs32
.f_infosector
);
237 h
->bs32
.f_backupboot
= ltohs(h
->bs32
.f_backupboot
);
239 h
->bps
= PC_BPSEC(h
);
245 valid_media(fstyp_pcfs_t
*h
)
247 switch (h
->bs
.mediadesriptor
) {
262 well_formed(fstyp_pcfs_t
*h
)
266 if (h
->bs16
.f_bootsig
== 0x29) {
267 fatmatch
= ((h
->bs16
.f_typestring
[0] == 'F' &&
268 h
->bs16
.f_typestring
[1] == 'A' &&
269 h
->bs16
.f_typestring
[2] == 'T') &&
270 (h
->bs
.fatsec
> 0) &&
271 ((PC_NSEC(h
) == 0 && h
->bs
.totalsec
> 0) ||
273 } else if (h
->bs32
.f_bootsig
== 0x29) {
274 fatmatch
= ((h
->bs32
.f_typestring
[0] == 'F' &&
275 h
->bs32
.f_typestring
[1] == 'A' &&
276 h
->bs32
.f_typestring
[2] == 'T') &&
277 (h
->bs
.fatsec
== 0 && h
->bs32
.f_fatlength
> 0) &&
278 ((PC_NSEC(h
) == 0 && h
->bs
.totalsec
> 0) ||
281 fatmatch
= (PC_NSEC(h
) > 0 && h
->bs
.fatsec
> 0);
284 return (fatmatch
&& h
->bps
> 0 && h
->bps
% 512 == 0 &&
285 h
->bs
.spcl
> 0 && PC_RESSEC(h
) >= 1 && h
->bs
.nfat
> 0);
289 calculate_parameters(fstyp_pcfs_t
*h
)
291 if (PC_NSEC(h
) != 0) {
292 h
->TotSec
= PC_NSEC(h
);
294 h
->TotSec
= h
->bs
.totalsec
;
296 if (h
->bs
.fatsec
!= 0) {
297 h
->FATSz
= h
->bs
.fatsec
;
299 h
->FATSz
= h
->bs32
.f_fatlength
;
301 if ((h
->bps
== 0) || (h
->bs
.spcl
== 0)) {
305 ((PC_NROOTENT(h
) * 32) + (h
->bps
- 1)) / h
->bps
;
307 PC_RESSEC(h
) + h
->bs
.nfat
* h
->FATSz
+ h
->RootDirSectors
;
308 h
->DataSec
= h
->TotSec
- h
->FirstDataSector
;
309 h
->CountOfClusters
= h
->DataSec
/ h
->bs
.spcl
;
313 determine_fattype(fstyp_pcfs_t
*h
)
315 if ((h
->CountOfClusters
>= 4085 && h
->CountOfClusters
<= 4095) ||
316 (h
->CountOfClusters
>= 65525 && h
->CountOfClusters
<= 65535)) {
318 } else if (h
->CountOfClusters
< 4085) {
320 } else if (h
->CountOfClusters
< 65525) {
328 get_label(fstyp_pcfs_t
*h
)
331 * Use label from the boot sector by default.
332 * Can overwrite later with the one from root directory.
334 (void) memcpy(h
->volume_label
, PC_LABEL_ADDR(h
), PC_LABEL_SIZE
);
335 h
->volume_label
[PC_LABEL_SIZE
] = '\0';
337 if (h
->fattype
== 0) {
339 } else if (FSTYP_IS_32(h
)) {
347 * Get volume label from the root directory entry.
348 * In FAT12/16 the root directory is of fixed size.
349 * It immediately follows the FATs
352 get_label_16(fstyp_pcfs_t
*h
)
354 ulong_t FirstRootDirSecNum
;
357 uint8_t buf
[PC_SECSIZE
* 4];
361 if ((secsize
= h
->bps
) > sizeof (buf
)) {
365 FirstRootDirSecNum
= PC_RESSEC(h
) + h
->bs
.nfat
* h
->bs
.fatsec
;
366 offset
= h
->offset
+ FirstRootDirSecNum
* secsize
;
367 resid
= PC_NROOTENT(h
);
369 for (i
= 0; i
< h
->RootDirSectors
; i
++) {
370 (void) lseek(h
->fd
, offset
, SEEK_SET
);
371 if (read(h
->fd
, buf
, secsize
) != secsize
) {
375 nent
= secsize
/ sizeof (struct pcdir
);
379 if (dir_find_label(h
, (struct pcdir
*)buf
, nent
)) {
384 offset
+= PC_SECSIZE
;
389 * Get volume label from the root directory entry.
390 * In FAT32 root is a usual directory, a cluster chain.
391 * It starts at BPB_RootClus.
394 get_label_32(fstyp_pcfs_t
*h
)
399 ulong_t FirstSectorofCluster
;
404 clustersize
= h
->bs
.spcl
* h
->bps
;
405 if ((clustersize
== 0) || (clustersize
> FSTYP_MAX_CLUSTER_SIZE
) ||
406 ((buf
= calloc(1, clustersize
)) == NULL
)) {
410 for (n
= h
->bs32
.f_rootcluster
; n
!= 0; n
= next_cluster_32(h
, n
)) {
411 FirstSectorofCluster
=
412 (n
- 2) * h
->bs
.spcl
+ h
->FirstDataSector
;
413 offset
= h
->offset
+ FirstSectorofCluster
* h
->bps
;
414 (void) lseek(h
->fd
, offset
, SEEK_SET
);
415 if (read(h
->fd
, buf
, clustersize
) != clustersize
) {
419 nent
= clustersize
/ sizeof (struct pcdir
);
420 if (dir_find_label(h
, (struct pcdir
*)buf
, nent
)) {
424 if (++cnt
> FSTYP_MAX_DIR_SIZE
/ clustersize
) {
433 * Get a FAT entry pointing to the next file cluster
436 next_cluster_32(fstyp_pcfs_t
*h
, int n
)
438 uint8_t buf
[PC_SECSIZE
];
439 ulong_t ThisFATSecNum
;
440 ulong_t ThisFATEntOffset
;
445 ThisFATSecNum
= PC_RESSEC(h
) + (n
* 4) / h
->bps
;
446 ThisFATEntOffset
= (n
* 4) % h
->bps
;
447 offset
= h
->offset
+ ThisFATSecNum
* h
->bps
;
449 (void) lseek(h
->fd
, offset
, SEEK_SET
);
450 if (read(h
->fd
, buf
, sizeof (buf
)) == sizeof (buf
)) {
451 val
= buf
[ThisFATEntOffset
] & 0x0fffffff;
459 * Given an array of pcdir structs, find one containing volume label.
462 dir_find_label(fstyp_pcfs_t
*h
, struct pcdir
*d
, int nent
)
466 for (i
= 0; i
< nent
; i
++, d
++) {
469 if ((d
->pcd_filename
[0] != PCD_UNUSED
) &&
470 (d
->pcd_filename
[0] != PCD_ERASED
) &&
471 ((d
->pcd_attr
& (PCA_LABEL
| PCA_DIR
)) == PCA_LABEL
) &&
472 (d
->un
.pcd_scluster_hi
== 0) &&
473 (d
->pcd_scluster_lo
== 0)) {
474 (void) memcpy(h
->volume_label
, d
->pcd_filename
,
476 h
->volume_label
[PC_LABEL_SIZE
] = '\0';
484 is_pcfs(fstyp_pcfs_t
*h
)
488 if ((error
= read_bootsec(h
)) != 0) {
491 if (!valid_media(h
)) {
492 return (FSTYP_ERR_NO_MATCH
);
494 if (!well_formed(h
)) {
495 return (FSTYP_ERR_NO_MATCH
);
498 calculate_parameters(h
);
499 determine_fattype(h
);
507 dumpfs(fstyp_pcfs_t
*h
, FILE *fout
, FILE *ferr
)
510 "Bytes Per Sector %d\t\tSectors Per Cluster %d\n",
513 "Reserved Sectors %d\t\tNumber of FATs %d\n",
514 (unsigned short)PC_RESSEC(h
), h
->bs
.nfat
);
516 "Root Dir Entries %d\t\tNumber of Sectors %d\n",
517 (unsigned short)PC_NROOTENT(h
), (unsigned short)PC_NSEC(h
));
519 "Sectors Per FAT %d\t\tSectors Per Track %d\n",
520 h
->bs
.fatsec
, h
->bs
.spt
);
522 "Number of Heads %d\t\tNumber Hidden Sectors %d\n",
523 h
->bs
.nhead
, h
->bs
.hiddensec
);
524 (void) fprintf(fout
, "Volume ID: 0x%x\n", PC_VOLID(h
));
525 (void) fprintf(fout
, "Volume Label: %s\n", h
->volume_label
);
526 (void) fprintf(fout
, "Drive Number: 0x%x\n", PC_DRVNUM(h
));
527 (void) fprintf(fout
, "Media Type: 0x%x ", h
->bs
.mediadesriptor
);
529 switch (h
->bs
.mediadesriptor
) {
531 (void) fprintf(fout
, "\"Fixed\" Disk\n");
534 (void) fprintf(fout
, "Single Sided, 8 Sectors Per Track\n");
537 (void) fprintf(fout
, "Double Sided, 8 Sectors Per Track\n");
540 (void) fprintf(fout
, "Single Sided, 9 Sectors Per Track\n");
543 (void) fprintf(fout
, "Double Sided, 9 Sectors Per Track\n");
546 (void) fprintf(fout
, "Double Sided, 18 Sectors Per Track\n");
549 (void) fprintf(fout
, "Double Sided, 9-15 Sectors Per Track\n");
552 (void) fprintf(fout
, "Unknown Media Type\n");
558 #define ADD_STRING(h, name, value) \
559 if (nvlist_add_string(h->attr, name, value) != 0) { \
560 return (FSTYP_ERR_NOMEM); \
563 #define ADD_UINT32(h, name, value) \
564 if (nvlist_add_uint32(h->attr, name, value) != 0) { \
565 return (FSTYP_ERR_NOMEM); \
568 #define ADD_UINT64(h, name, value) \
569 if (nvlist_add_uint64(h->attr, name, value) != 0) { \
570 return (FSTYP_ERR_NOMEM); \
573 #define ADD_BOOL(h, name, value) \
574 if (nvlist_add_boolean_value(h->attr, name, value) != 0) { \
575 return (FSTYP_ERR_NOMEM); \
579 get_attr(fstyp_pcfs_t
*h
)
583 ADD_UINT32(h
, "bytes_per_sector", h
->bps
);
584 ADD_UINT32(h
, "sectors_per_cluster", h
->bs
.spcl
);
585 ADD_UINT32(h
, "reserved_sectors", PC_RESSEC(h
));
586 ADD_UINT32(h
, "fats", h
->bs
.nfat
);
587 ADD_UINT32(h
, "root_entry_count", PC_NROOTENT(h
));
588 ADD_UINT32(h
, "total_sectors_16", PC_NSEC(h
));
589 ADD_UINT32(h
, "media", h
->bs
.mediadesriptor
);
590 ADD_UINT32(h
, "fat_size_16", h
->bs
.fatsec
);
591 ADD_UINT32(h
, "sectors_per_track", h
->bs
.spt
);
592 ADD_UINT32(h
, "heads", h
->bs
.nhead
);
593 ADD_UINT32(h
, "hidden_sectors", h
->bs
.hiddensec
);
594 ADD_UINT32(h
, "total_sectors_32", h
->bs
.totalsec
);
595 ADD_UINT32(h
, "drive_number", PC_DRVNUM(h
));
596 ADD_UINT32(h
, "volume_id", PC_VOLID(h
));
597 ADD_STRING(h
, "volume_label", h
->volume_label
);
598 if (FSTYP_IS_32(h
)) {
599 ADD_UINT32(h
, "fat_size_32", h
->bs32
.f_fatlength
);
601 ADD_UINT32(h
, "total_sectors", h
->TotSec
);
602 ADD_UINT32(h
, "fat_size", h
->FATSz
);
603 ADD_UINT32(h
, "count_of_clusters", h
->CountOfClusters
);
604 ADD_UINT32(h
, "fat_entry_size", h
->fattype
);
606 ADD_BOOL(h
, "gen_clean", B_TRUE
);
607 if (PC_VOLID(a
) != 0) {
608 (void) snprintf(s
, sizeof (s
), "%08x", PC_VOLID(a
));
609 ADD_STRING(h
, "gen_guid", s
);
611 (void) snprintf(s
, sizeof (s
), "%d", h
->fattype
);
612 ADD_STRING(h
, "gen_version", s
);
613 ADD_STRING(h
, "gen_volume_label", h
->volume_label
);