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