dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / fs.d / pcfs / fstyp / fstyp.c
blob8448f7867c22b4957bb0ac19beded7b7c7253172
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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>
42 #include <errno.h>
43 #include <sys/fs/pc_fs.h>
44 #include <sys/fs/pc_label.h>
45 #include <sys/fs/pc_dir.h>
46 #include <sys/stat.h>
47 #include <sys/vfs.h>
48 #include <stdio.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include <strings.h>
52 #include <sys/mnttab.h>
53 #include <locale.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 bootsec {
62 uchar_t instr[3];
63 uchar_t version[8];
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 {
79 uint8_t f_drvnum;
80 uint8_t f_reserved1;
81 uint8_t f_bootsig;
82 uint8_t f_volid[4];
83 uint8_t f_label[11];
84 uint8_t f_typestring[8];
87 struct fstyp_fat32_bs {
88 uint32_t f_fatlength;
89 uint16_t f_flags;
90 uint8_t f_major;
91 uint8_t f_minor;
92 uint32_t f_rootcluster;
93 uint16_t f_infosector;
94 uint16_t f_backupboot;
95 uint8_t f_reserved2[12];
96 uint8_t f_drvnum;
97 uint8_t f_reserved1;
98 uint8_t f_bootsig;
99 uint8_t f_volid[4];
100 uint8_t f_label[11];
101 uint8_t f_typestring[8];
104 typedef struct fstyp_pcfs {
105 int fd;
106 off_t offset;
107 nvlist_t *attr;
108 struct bootsec bs;
109 struct fstyp_fat16_bs bs16;
110 struct fstyp_fat32_bs bs32;
111 ushort_t bps;
112 int fattype;
113 char volume_label[PC_LABEL_SIZE + 1];
115 /* parameters derived or calculated per FAT spec */
116 ulong_t FATSz;
117 ulong_t TotSec;
118 ulong_t RootDirSectors;
119 ulong_t FirstDataSector;
120 ulong_t DataSec;
121 ulong_t CountOfClusters;
122 } fstyp_pcfs_t;
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 : \
131 (h)->bs16.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);
170 h->fd = fd;
171 h->offset = offset;
173 *handle = (fstyp_mod_handle_t)h;
174 return (0);
177 void
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);
184 h->attr = NULL;
186 free(h);
190 fstyp_mod_ident(fstyp_mod_handle_t handle)
192 struct fstyp_pcfs *h = (struct fstyp_pcfs *)handle;
194 return (is_pcfs(h));
198 fstyp_mod_get_attr(fstyp_mod_handle_t handle, nvlist_t **attrp)
200 struct fstyp_pcfs *h = (struct fstyp_pcfs *)handle;
201 int error;
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);
209 h->attr = NULL;
210 return (error);
214 *attrp = h->attr;
215 return (0);
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.
230 static int
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);
258 return (0);
261 static int
262 valid_media(fstyp_pcfs_t *h)
264 switch (h->bs.mediadesriptor) {
265 case MD_FIXED:
266 case SS8SPT:
267 case DS8SPT:
268 case SS9SPT:
269 case DS9SPT:
270 case DS18SPT:
271 case DS9_15SPT:
272 return (1);
273 default:
274 return (0);
278 static int
279 well_formed(fstyp_pcfs_t *h)
281 int fatmatch;
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) ||
289 PC_NSEC(h) > 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) ||
296 PC_NSEC(h) > 0));
297 } else {
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);
305 static void
306 calculate_parameters(fstyp_pcfs_t *h)
308 if (PC_NSEC(h) != 0) {
309 h->TotSec = PC_NSEC(h);
310 } else {
311 h->TotSec = h->bs.totalsec;
313 if (h->bs.fatsec != 0) {
314 h->FATSz = h->bs.fatsec;
315 } else {
316 h->FATSz = h->bs32.f_fatlength;
318 if ((h->bps == 0) || (h->bs.spcl == 0)) {
319 return;
321 h->RootDirSectors =
322 ((PC_NROOTENT(h) * 32) + (h->bps - 1)) / h->bps;
323 h->FirstDataSector =
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;
329 static void
330 determine_fattype(fstyp_pcfs_t *h)
332 if ((h->CountOfClusters >= 4085 && h->CountOfClusters <= 4095) ||
333 (h->CountOfClusters >= 65525 && h->CountOfClusters <= 65535)) {
334 h->fattype = 0;
335 } else if (h->CountOfClusters < 4085) {
336 h->fattype = 12;
337 } else if (h->CountOfClusters < 65525) {
338 h->fattype = 16;
339 } else {
340 h->fattype = 32;
344 static void
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) {
355 return;
356 } else if (FSTYP_IS_32(h)) {
357 get_label_32(h);
358 } else {
359 get_label_16(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
368 static void
369 get_label_16(fstyp_pcfs_t *h)
371 ulong_t FirstRootDirSecNum;
372 int secsize;
373 off_t offset;
374 uint8_t buf[PC_SECSIZE * 4];
375 int i;
376 int nent, resid;
378 if ((secsize = h->bps) > sizeof (buf)) {
379 return;
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) {
389 return;
392 nent = secsize / sizeof (struct pcdir);
393 if (nent > resid) {
394 nent = resid;
396 if (dir_find_label(h, (struct pcdir *)buf, nent)) {
397 return;
400 resid -= 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.
410 static void
411 get_label_32(fstyp_pcfs_t *h)
413 off_t offset;
414 int clustersize;
415 int n;
416 ulong_t FirstSectorofCluster;
417 uint8_t *buf;
418 int nent;
419 int cnt = 0;
421 clustersize = h->bs.spcl * h->bps;
422 if ((clustersize == 0) || (clustersize > FSTYP_MAX_CLUSTER_SIZE) ||
423 ((buf = calloc(1, clustersize)) == NULL)) {
424 return;
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) {
433 break;
436 nent = clustersize / sizeof (struct pcdir);
437 if (dir_find_label(h, (struct pcdir *)buf, nent)) {
438 break;
441 if (++cnt > FSTYP_MAX_DIR_SIZE / clustersize) {
442 break;
446 free(buf);
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;
458 off_t offset;
459 uint32_t val;
460 int next = 0;
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;
469 next = ltohi(val);
472 return (next);
476 * Given an array of pcdir structs, find one containing volume label.
478 static boolean_t
479 dir_find_label(fstyp_pcfs_t *h, struct pcdir *d, int nent)
481 int i;
483 for (i = 0; i < nent; i++, d++) {
484 if (PCDL_IS_LFN(d))
485 continue;
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,
492 PC_LABEL_SIZE);
493 h->volume_label[PC_LABEL_SIZE] = '\0';
494 return (B_TRUE);
497 return (B_FALSE);
500 static int
501 is_pcfs(fstyp_pcfs_t *h)
503 int error;
505 if ((error = read_bootsec(h)) != 0) {
506 return (error);
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);
517 get_label(h);
519 return (0);
522 /* ARGSUSED */
523 static int
524 dumpfs(fstyp_pcfs_t *h, FILE *fout, FILE *ferr)
526 (void) fprintf(fout,
527 "Bytes Per Sector %d\t\tSectors Per Cluster %d\n",
528 h->bps, h->bs.spcl);
529 (void) fprintf(fout,
530 "Reserved Sectors %d\t\tNumber of FATs %d\n",
531 (unsigned short)PC_RESSEC(h), h->bs.nfat);
532 (void) fprintf(fout,
533 "Root Dir Entries %d\t\tNumber of Sectors %d\n",
534 (unsigned short)PC_NROOTENT(h), (unsigned short)PC_NSEC(h));
535 (void) fprintf(fout,
536 "Sectors Per FAT %d\t\tSectors Per Track %d\n",
537 h->bs.fatsec, h->bs.spt);
538 (void) fprintf(fout,
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) {
547 case MD_FIXED:
548 (void) fprintf(fout, "\"Fixed\" Disk\n");
549 break;
550 case SS8SPT:
551 (void) fprintf(fout, "Single Sided, 8 Sectors Per Track\n");
552 break;
553 case DS8SPT:
554 (void) fprintf(fout, "Double Sided, 8 Sectors Per Track\n");
555 break;
556 case SS9SPT:
557 (void) fprintf(fout, "Single Sided, 9 Sectors Per Track\n");
558 break;
559 case DS9SPT:
560 (void) fprintf(fout, "Double Sided, 9 Sectors Per Track\n");
561 break;
562 case DS18SPT:
563 (void) fprintf(fout, "Double Sided, 18 Sectors Per Track\n");
564 break;
565 case DS9_15SPT:
566 (void) fprintf(fout, "Double Sided, 9-15 Sectors Per Track\n");
567 break;
568 default:
569 (void) fprintf(fout, "Unknown Media Type\n");
572 return (0);
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); \
595 static int
596 get_attr(fstyp_pcfs_t *h)
598 char s[64];
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);
632 return (0);