1/* 2 * Copyright (c) 2000-2005,2008,2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * Copyright (c) 1998 Robert Nordier 25 * All rights reserved. 26 * 27 * Redistribution and use in source and binary forms, with or without 28 * modification, are permitted provided that the following conditions 29 * are met: 30 * 1. Redistributions of source code must retain the above copyright 31 * notice, this list of conditions and the following disclaimer. 32 * 2. Redistributions in binary form must reproduce the above copyright 33 * notice, this list of conditions and the following disclaimer in 34 * the documentation and/or other materials provided with the 35 * distribution. 36 * 37 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 38 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 39 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY 41 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 43 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 44 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 45 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 46 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 47 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 48 */ 49 50/* @(#)dosutil.c 3.0 13/09/00 (c) 2000 Apple Computer, Inc. */ 51 52#include <stdint.h> 53#include <mach/machine/boolean.h> 54 55#include <sys/types.h> 56#include <sys/param.h> 57#include <sys/wait.h> 58#include <sys/errno.h> 59#include <sys/stat.h> 60#include <sys/mount.h> 61#include <sys/sysctl.h> 62#include <sys/loadable_fs.h> 63 64#include <sys/disk.h> 65 66#include <machine/byte_order.h> 67 68#include <err.h> 69#include <sysexits.h> 70#include <fcntl.h> 71#include <ctype.h> 72#include <stdio.h> 73#include <unistd.h> 74#include <string.h> 75#include <stdlib.h> 76#include <pwd.h> 77 78#include <CoreFoundation/CFString.h> 79#include <CoreFoundation/CFStringEncodingExt.h> 80 81#include "../msdosfs.kextproj/msdosfs.kmodproj/bootsect.h" 82#include "../msdosfs.kextproj/msdosfs.kmodproj/bpb.h" 83#include "../msdosfs.kextproj/msdosfs.kmodproj/direntry.h" 84 85#define FS_TYPE "msdos" 86#define FS_NAME_FILE "MSDOS" 87#define FS_BUNDLE_NAME "msdosfs.kext" 88#define FS_KEXT_DIR "/System/Library/Extensions/msdosfs.kext" 89#define FS_KMOD_DIR "/System/Library/Extensions/msdosfs.kext/msdosfs" 90#define RAWDEV_PREFIX "/dev/r" 91#define BLOCKDEV_PREFIX "/dev/" 92#define MOUNT_COMMAND "/sbin/mount" 93#define UMOUNT_COMMAND "/sbin/umount" 94#define KEXTLOAD_COMMAND "/sbin/kextload" 95#define KMODLOAD_COMMAND "/sbin/kmodload" 96#define READWRITE_OPT "-w" 97#define READONLY_OPT "-r" 98#define SUID_OPT "suid" 99#define NOSUID_OPT "nosuid" 100#define DEV_OPT "dev" 101#define NODEV_OPT "nodev" 102#define LABEL_LENGTH 11 103#define MAX_DOS_BLOCKSIZE 4096 104 105#define FSUC_LABEL 'n' 106 107#define UNKNOWN_LABEL "Unlabeled" 108 109#define DEVICE_SUID "suid" 110#define DEVICE_NOSUID "nosuid" 111 112#define DEVICE_DEV "dev" 113#define DEVICE_NODEV "nodev" 114 115#define CLUST_FIRST 2 /* first legal cluster number */ 116#define CLUST_RSRVD 0x0ffffff6 /* reserved cluster range */ 117 118 119 120/* globals */ 121const char *progname; /* our program name, from argv[0] */ 122int debug; /* use -D to enable debug printfs */ 123 124 125 126/* 127 * The following code is re-usable for all FS_util programs 128 */ 129void usage(void); 130 131static int fs_probe(char *devpath, int removable, int writable); 132static int fs_mount(char *devpath, char *mount_point, int removable, 133 int writable, int suid, int dev); 134static int fs_unmount(char *devpath); 135static int fs_label(char *devpath, char *volName); 136static void fs_set_label_file(char *labelPtr); 137 138static int safe_open(char *path, int flags, mode_t mode); 139static void safe_read(int fd, void *buf, int nbytes, off_t off); 140static void safe_close(int fd); 141static void safe_write(int fd, char *data, int len, off_t off); 142static void safe_execv(const char *args[]); 143 144static int checkLoadable(void); 145static int oklabel(const char *src); 146static void mklabel(char *dest, const char *src); 147 148int ret = 0; 149 150 151void usage() 152{ 153 fprintf(stderr, "usage: %s action_arg device_arg [mount_point_arg] [Flags]\n", progname); 154 fprintf(stderr, "action_arg:\n"); 155 fprintf(stderr, " -%c (Probe)\n", FSUC_PROBE); 156 fprintf(stderr, " -%c (Mount)\n", FSUC_MOUNT); 157 fprintf(stderr, " -%c (Unmount)\n", FSUC_UNMOUNT); 158 fprintf(stderr, " -%c name\n", 'n'); 159 fprintf(stderr, "device_arg:\n"); 160 fprintf(stderr, " device we are acting upon (for example, 'disk0s2')\n"); 161 fprintf(stderr, "mount_point_arg:\n"); 162 fprintf(stderr, " required for Mount and Force Mount \n"); 163 fprintf(stderr, "Flags:\n"); 164 fprintf(stderr, " required for Mount, Force Mount and Probe\n"); 165 fprintf(stderr, " indicates removable or fixed (for example 'fixed')\n"); 166 fprintf(stderr, " indicates readonly or writable (for example 'readonly')\n"); 167 fprintf(stderr, "Examples:\n"); 168 fprintf(stderr, " %s -p disk0s2 fixed writable\n", progname); 169 fprintf(stderr, " %s -m disk0s2 /my/hfs removable readonly\n", progname); 170 exit(FSUR_INVAL); 171} 172 173int main(int argc, char **argv) 174{ 175 char rawdevpath[MAXPATHLEN]; 176 char blockdevpath[MAXPATHLEN]; 177 char opt; 178 struct stat sb; 179 int ret = FSUR_INVAL; 180 181 182 /* save & strip off program name */ 183 progname = argv[0]; 184 argc--; 185 argv++; 186 187 /* secret debug flag - must be 1st flag */ 188 debug = (argc > 0 && !strcmp(argv[0], "-D")); 189 if (debug) { /* strip off debug flag argument */ 190 argc--; 191 argv++; 192 } 193 194 if (argc < 2 || argv[0][0] != '-') 195 usage(); 196 opt = argv[0][1]; 197 if (opt != FSUC_PROBE && opt != FSUC_MOUNT && opt != FSUC_UNMOUNT && opt != FSUC_LABEL) 198 usage(); /* Not supported action */ 199 if ((opt == FSUC_MOUNT || opt == FSUC_UNMOUNT || opt == FSUC_LABEL) && argc < 3) 200 usage(); /* mountpoint arg missing! */ 201 202 if (snprintf(rawdevpath, sizeof(rawdevpath), "%s%s", RAWDEV_PREFIX, argv[1]) >= sizeof(rawdevpath)) 203 exit(FSUR_INVAL); 204 if (stat(rawdevpath, &sb) != 0) { 205 fprintf(stderr, "%s: stat %s failed, %s\n", progname, rawdevpath, 206 strerror(errno)); 207 exit(FSUR_INVAL); 208 } 209 210 if (snprintf(blockdevpath, sizeof(blockdevpath), "%s%s", BLOCKDEV_PREFIX, argv[1]) >= sizeof(blockdevpath)) 211 exit(FSUR_INVAL); 212 if (stat(blockdevpath, &sb) != 0) { 213 fprintf(stderr, "%s: stat %s failed, %s\n", progname, blockdevpath, 214 strerror(errno)); 215 exit(FSUR_INVAL); 216 } 217 218 switch (opt) { 219 case FSUC_PROBE: { 220 if (argc != 4) 221 usage(); 222 ret = fs_probe(rawdevpath, 223 strcmp(argv[2], DEVICE_FIXED), 224 strcmp(argv[3], DEVICE_READONLY)); 225 break; 226 } 227 228 case FSUC_MOUNT: 229 case FSUC_MOUNT_FORCE: 230 if (argc != 7) 231 usage(); 232 if (strcmp(argv[3], DEVICE_FIXED) && strcmp(argv[3], DEVICE_REMOVABLE)) { 233 printf("msdosfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",3,argv[3]); 234 usage(); 235 } 236 if (strcmp(argv[4], DEVICE_READONLY) && strcmp(argv[4], DEVICE_WRITABLE)) { 237 printf("msdosfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",4,argv[4]); 238 usage(); 239 } 240 if (strcmp(argv[5], DEVICE_SUID) && strcmp(argv[5], DEVICE_NOSUID)) { 241 printf("msdosfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",5,argv[5]); 242 usage(); 243 } 244 if (strcmp(argv[6], DEVICE_DEV) && strcmp(argv[6], DEVICE_NODEV)) { 245 printf("msdosfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",6,argv[6]); 246 usage(); 247 } 248 ret = fs_mount(blockdevpath, 249 argv[2], 250 strcmp(argv[3], DEVICE_FIXED), 251 strcmp(argv[4], DEVICE_READONLY), 252 strcmp(argv[5], DEVICE_NOSUID), 253 strcmp(argv[6], DEVICE_NODEV)); 254 break; 255 case FSUC_UNMOUNT: 256 ret = fs_unmount(rawdevpath); 257 break; 258 case FSUC_LABEL: 259 ret = fs_label(rawdevpath, argv[2]); 260 break; 261 default: 262 usage(); 263 } 264 265 exit(ret); 266 267 return(ret); 268} 269 270/* 271 * Begin Filesystem-specific code 272 */ 273static int fs_probe(char *devpath, int removable, int writable) 274{ 275 int fd; 276 struct dosdirentry *dirp; 277 union bootsector *bsp; 278 struct byte_bpb33 *b33; 279 struct byte_bpb50 *b50; 280 struct byte_bpb710 *b710; 281 u_int32_t dev_block_size; 282 u_int16_t bps; 283 u_int8_t spc; 284 unsigned rootDirSectors; 285 unsigned i,j, finished; 286 char diskLabel[LABEL_LENGTH]; 287 char buf[MAX_DOS_BLOCKSIZE]; 288 289 fd = safe_open(devpath, O_RDONLY, 0); 290 291 if (ioctl(fd, DKIOCGETBLOCKSIZE, &dev_block_size) < 0) 292 { 293 fprintf(stderr, "%s: ioctl(DKIOCGETBLOCKSIZE) for %s failed, %s\n", 294 progname, devpath, strerror(errno)); 295 return FSUR_IO_FAIL; 296 } 297 if (dev_block_size > MAX_DOS_BLOCKSIZE) 298 { 299 fprintf(stderr, "%s: block size of %s is too big (%lu)\n", 300 progname, devpath, (unsigned long) dev_block_size); 301 return FSUR_UNRECOGNIZED; 302 } 303 304 /* 305 * Read the boot sector of the filesystem, and then check the 306 * boot signature. If not a dos boot sector then error out. 307 * 308 * NOTE: 4096 is a maximum sector size in current... 309 */ 310 safe_read(fd, buf, MAX_DOS_BLOCKSIZE, 0); 311 312 bsp = (union bootsector *)buf; 313 b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; 314 b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; 315 b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; 316 317 /* [2699033] 318 * 319 * The first three bytes are an Intel x86 jump instruction. It should be one 320 * of the following forms: 321 * 0xE9 0x?? 0x?? 322 * 0xEB 0x?? 0x90 323 * where 0x?? means any byte value is OK. 324 * 325 * [5016947] 326 * 327 * Windows doesn't actually check the third byte if the first byte is 0xEB, 328 * so we don't either 329 */ 330 if (bsp->bs50.bsJump[0] != 0xE9 331 && bsp->bs50.bsJump[0] != 0xEB) 332 { 333 return FSUR_UNRECOGNIZED; 334 } 335 336 /* It is possible that the above check could match a partition table, or some */ 337 /* non-FAT disk meant to boot a PC. Check some more fields for sensible values. */ 338 339 /* We only work with 512, 1024, 2048, and 4096 byte sectors */ 340 bps = getuint16(b33->bpbBytesPerSec); 341 if ((bps < 0x200) || (bps & (bps - 1)) || (bps > MAX_DOS_BLOCKSIZE)) 342 { 343 return(FSUR_UNRECOGNIZED); 344 } 345 346 /* Check to make sure valid sectors per cluster */ 347 spc = b33->bpbSecPerClust; 348 if ((spc == 0 ) || (spc & (spc - 1))) 349 { 350 return(FSUR_UNRECOGNIZED); 351 } 352 353 /* Make sure the number of FATs is OK; on NTFS, this will be zero */ 354 if (b33->bpbFATs == 0) 355 { 356 return(FSUR_UNRECOGNIZED); 357 } 358 359 /* Make sure the total sectors is non-zero */ 360 if (getuint16(b33->bpbSectors) == 0 && getuint32(b50->bpbHugeSectors) == 0) 361 { 362 return(FSUR_UNRECOGNIZED); 363 } 364 365 /* Make sure there is a root directory */ 366 if (getuint16(b33->bpbRootDirEnts) == 0 && getuint32(b710->bpbRootClust) == 0) 367 { 368 return(FSUR_UNRECOGNIZED); 369 } 370 371 /* we know this disk, find the volume label */ 372 /* First, find the root directory */ 373 diskLabel[0] = 0; 374 finished = false; 375 rootDirSectors = ((getuint16(b50->bpbRootDirEnts) * sizeof(struct dosdirentry)) + 376 (bps-1)) / bps; 377 if (rootDirSectors) { /* FAT12 or FAT16 */ 378 unsigned firstRootDirSecNum; 379 char rootdirbuf[MAX_DOS_BLOCKSIZE]; 380 381 firstRootDirSecNum = getuint16(b33->bpbResSectors) + (b33->bpbFATs * getuint16(b33->bpbFATsecs)); 382 for (i=0; i< rootDirSectors; i++) { 383 safe_read(fd, rootdirbuf, bps, (firstRootDirSecNum+i)*bps); 384 dirp = (struct dosdirentry *)rootdirbuf; 385 for (j=0; j<bps; j+=sizeof(struct dosdirentry), dirp++) { 386 if (dirp->deName[0] == SLOT_EMPTY) { 387 finished = true; 388 break; 389 } 390 else if (dirp->deName[0] == SLOT_DELETED) 391 continue; 392 else if (dirp->deAttributes == ATTR_WIN95) 393 continue; 394 else if (dirp->deAttributes & ATTR_VOLUME) { 395 strncpy(diskLabel, (char*)dirp->deName, LABEL_LENGTH); 396 finished = true; 397 break; 398 } 399 } /* j */ 400 if (finished == true) 401 break; 402 } /* i */ 403 } 404 else { /* FAT32 */ 405 u_int32_t cluster; 406 u_int32_t bytesPerCluster; 407 u_int8_t *rootDirBuffer; 408 off_t readOffset; 409 410 bytesPerCluster = (u_int32_t) bps * (u_int32_t) spc; 411 rootDirBuffer = malloc(bytesPerCluster); 412 cluster = getuint32(b710->bpbRootClust); 413 414 finished = false; 415 while (!finished && cluster >= CLUST_FIRST && cluster < CLUST_RSRVD) 416 { 417 /* Find sector where clusters start */ 418 readOffset = getuint16(b710->bpbResSectors) + (b710->bpbFATs * getuint32(b710->bpbBigFATsecs)); 419 /* Find sector where "cluster" starts */ 420 readOffset += ((off_t) cluster - CLUST_FIRST) * (off_t) spc; 421 /* Convert to byte offset */ 422 readOffset *= (off_t) bps; 423 424 /* Read in "cluster" */ 425 safe_read(fd, rootDirBuffer, bytesPerCluster, readOffset); 426 dirp = (struct dosdirentry *) rootDirBuffer; 427 428 /* Examine each directory entry in this cluster */ 429 for (i=0; i < bytesPerCluster; i += sizeof(struct dosdirentry), dirp++) 430 { 431 if (dirp->deName[0] == SLOT_EMPTY) { 432 finished = true; // Reached end of directory (never used entry) 433 break; 434 } 435 else if (dirp->deName[0] == SLOT_DELETED) 436 continue; 437 else if (dirp->deAttributes == ATTR_WIN95) 438 continue; 439 else if (dirp->deAttributes & ATTR_VOLUME) { 440 strncpy(diskLabel, (char *)dirp->deName, LABEL_LENGTH); 441 finished = true; 442 break; 443 } 444 } 445 if (finished) 446 break; 447 448 /* Find next cluster in the chain by reading the FAT */ 449 450 /* Find first sector of FAT */ 451 readOffset = getuint16(b710->bpbResSectors); 452 /* Find sector containing "cluster" entry in FAT */ 453 readOffset += (cluster * 4) / bps; 454 /* Convert to byte offset */ 455 readOffset *= bps; 456 457 /* Read one sector of the FAT */ 458 safe_read(fd, rootDirBuffer, bps, readOffset); 459 460 cluster = getuint32(rootDirBuffer + ((cluster * 4) % bps)); 461 cluster &= 0x0FFFFFFF; // ignore reserved upper bits 462 } 463 free(rootDirBuffer); 464 } /* rootDirSectors */ 465 466 /* else look in the boot blocks */ 467 if (diskLabel[0] == 0) { 468 if (getuint16(b50->bpbRootDirEnts) == 0) { /* Its a FAT32 */ 469 if (((struct extboot *)bsp->bs710.bsExt)->exBootSignature == EXBOOTSIG) { 470 strncpy(diskLabel, (char *)((struct extboot *)bsp->bs710.bsExt)->exVolumeLabel, LABEL_LENGTH); 471 } 472 } 473 else if (((struct extboot *)bsp->bs50.bsExt)->exBootSignature == EXBOOTSIG) { 474 strncpy(diskLabel, (char *)((struct extboot *)bsp->bs50.bsExt)->exVolumeLabel, LABEL_LENGTH); 475 } 476 } 477 478 fs_set_label_file(diskLabel); 479 480 safe_close(fd); 481 482 return(FSUR_RECOGNIZED); 483} 484 485 486static int fs_mount(char *devpath, char *mount_point, int removable, int writable, int suid, int dev) { 487 const char *kextargs[] = {KEXTLOAD_COMMAND, FS_KEXT_DIR, NULL}; 488 const char *mountargs[] = {MOUNT_COMMAND, READWRITE_OPT, "-o", SUID_OPT, "-o", 489 DEV_OPT, "-t", FS_TYPE, devpath, mount_point, NULL}; 490 491 if (! writable) 492 mountargs[1] = READONLY_OPT; 493 494 if (! suid) 495 mountargs[3] = NOSUID_OPT; 496 497 if (! dev) 498 mountargs[5] = NODEV_OPT; 499 500 if (checkLoadable()) 501 safe_execv(kextargs); /* better here than in mount_udf */ 502 safe_execv(mountargs); 503 ret = FSUR_IO_SUCCESS; 504 505 return ret; 506} 507 508static int fs_unmount(char *devpath) { 509 const char *umountargs[] = {UMOUNT_COMMAND, devpath, NULL}; 510 511 safe_execv(umountargs); 512 return(FSUR_IO_SUCCESS); 513} 514 515 516 517/* 518 * Begin Filesystem-specific code 519 */ 520static int fs_label(char *devpath, char *volName) 521{ 522 int fd; 523 union bootsector *bsp; 524 struct byte_bpb33 *b33; 525 struct byte_bpb50 *b50; 526 u_int16_t bps; 527 u_int8_t spc; 528 char tmplabel[LABEL_LENGTH], label[LABEL_LENGTH]; 529 char buf[MAX_DOS_BLOCKSIZE]; 530 CFStringRef cfstr; 531 532 533 /* First normalize the label */ 534 if (volName == NULL) 535 errx(EX_USAGE, "No label was given"); 536 537 /* Convert it from UTF-8 */ 538 cfstr = CFStringCreateWithCString(kCFAllocatorDefault, volName, kCFStringEncodingUTF8); 539 if (cfstr == NULL) 540 errx(EX_DATAERR, "Bad UTF8 Name"); 541 if (! CFStringGetCString(cfstr, tmplabel, LABEL_LENGTH, kCFStringEncodingWindowsLatin1)) 542 errx(EX_DATAERR, "Could not convert to DOS Latin1"); 543 CFRelease(cfstr); 544 545 if (! oklabel(tmplabel)) 546 errx(EX_DATAERR, "Label has illegal characters"); 547 548 /* Finall format it */ 549 mklabel(label, tmplabel); 550 551 552 fd = safe_open(devpath, O_RDWR, 0); 553 554 /* 555 * Read the boot sector of the filesystem, and then check the 556 * boot signature. If not a dos boot sector then error out. 557 * 558 * NOTE: 4096 is a maximum sector size in current... 559 */ 560 safe_read(fd, buf, MAX_DOS_BLOCKSIZE, 0); 561 562 bsp = (union bootsector *)buf; 563 b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; 564 b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; 565 566 if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 567 || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { 568 return(FSUR_UNRECOGNIZED); 569 } 570 571 /* Both partitions tables and boot sectors pass the above test, do do some more */ 572 573 /* We only work with 512, 1024, 2048 and 4096 byte sectors */ 574 bps = getuint16(b33->bpbBytesPerSec); 575 if ((bps < 0x200) || (bps & (bps - 1)) || (bps > MAX_DOS_BLOCKSIZE)) 576 return(FSUR_UNRECOGNIZED); 577 578 /* Check to make sure valid sectors per cluster */ 579 spc = b33->bpbSecPerClust; 580 if ((spc == 0 ) || (spc & (spc - 1))) 581 return(FSUR_UNRECOGNIZED); 582 583 /* we know this disk, find the volume label */ 584 if (getuint16(b50->bpbRootDirEnts) == 0) { 585 /* Its a FAT32 */ 586 strncpy((char *)((struct extboot *)bsp->bs710.bsExt)->exVolumeLabel, label, LABEL_LENGTH); 587 } 588 else if (((struct extboot *)bsp->bs50.bsExt)->exBootSignature == EXBOOTSIG) { 589 strncpy((char *)((struct extboot *)bsp->bs50.bsExt)->exVolumeLabel, label, LABEL_LENGTH); 590 } 591 592 593 safe_write(fd, buf, MAX_DOS_BLOCKSIZE, 0); 594 595 safe_close(fd); 596 597 return(FSUR_IO_SUCCESS); 598} 599 600 601static CFStringEncoding GetDefaultDOSEncoding(void) 602{ 603 CFStringEncoding encoding; 604 struct passwd *passwdp; 605 int fd; 606 ssize_t size; 607 char buffer[MAXPATHLEN + 1]; 608 609 /* 610 * Get a default (Mac) encoding. We use the CFUserTextEncoding 611 * file since CFStringGetSystemEncoding() always seems to 612 * return 0 when msdos.util is executed via disk arbitration. 613 */ 614 encoding = kCFStringEncodingMacRoman; /* Default to Roman/Latin */ 615 if ((passwdp = getpwuid(getuid()))) { 616 strlcpy(buffer, passwdp->pw_dir, sizeof(buffer)); 617 strlcat(buffer, "/.CFUserTextEncoding", sizeof(buffer)); 618 619 if ((fd = open(buffer, O_RDONLY, 0)) > 0) { 620 size = read(fd, buffer, MAXPATHLEN); 621 buffer[(size < 0 ? 0 : size)] = '\0'; 622 close(fd); 623 encoding = (CFStringEncoding)strtol(buffer, NULL, 0); 624 } 625 } 626 627 /* Convert the Mac encoding to a DOS/Windows encoding. */ 628 switch (encoding) { 629 case kCFStringEncodingMacRoman: 630 encoding = kCFStringEncodingDOSLatin1; 631 break; 632 case kCFStringEncodingMacJapanese: 633 encoding = kCFStringEncodingDOSJapanese; 634 break; 635 case kCFStringEncodingMacChineseTrad: 636 encoding = kCFStringEncodingDOSChineseTrad; 637 break; 638 case kCFStringEncodingMacKorean: 639 encoding = kCFStringEncodingDOSKorean; 640 break; 641 case kCFStringEncodingMacArabic: 642 encoding = kCFStringEncodingDOSArabic; 643 break; 644 case kCFStringEncodingMacHebrew: 645 encoding = kCFStringEncodingDOSHebrew; 646 break; 647 case kCFStringEncodingMacGreek: 648 encoding = kCFStringEncodingDOSGreek; 649 break; 650 case kCFStringEncodingMacCyrillic: 651 case kCFStringEncodingMacUkrainian: 652 encoding = kCFStringEncodingDOSCyrillic; 653 break; 654 case kCFStringEncodingMacThai: 655 encoding = kCFStringEncodingDOSThai; 656 break; 657 case kCFStringEncodingMacChineseSimp: 658 encoding = kCFStringEncodingDOSChineseSimplif; 659 break; 660 case kCFStringEncodingMacCentralEurRoman: 661 case kCFStringEncodingMacCroatian: 662 case kCFStringEncodingMacRomanian: 663 encoding = kCFStringEncodingDOSLatin2; 664 break; 665 case kCFStringEncodingMacTurkish: 666 encoding = kCFStringEncodingDOSTurkish; 667 break; 668 case kCFStringEncodingMacIcelandic: 669 encoding = kCFStringEncodingDOSIcelandic; 670 break; 671 case kCFStringEncodingMacFarsi: 672 encoding = kCFStringEncodingDOSArabic; 673 break; 674 default: 675 encoding = kCFStringEncodingInvalidId; /* Error: no corresponding Windows encoding */ 676 break; 677 } 678 679 return encoding; 680} 681 682/* Set the name of this file system */ 683static void fs_set_label_file(char *labelPtr) 684{ 685 int i; 686 CFStringEncoding encoding; 687 char label[LABEL_LENGTH+1]; 688 char labelUTF8[LABEL_LENGTH*3]; 689 CFStringRef cfstr; 690 691 /* Make a local copy of the label */ 692 strncpy(label, labelPtr, LABEL_LENGTH); 693 label[LABEL_LENGTH] = 0; 694 695 /* Convert leading 0x05 to 0xE5 for multibyte languages like Japanese */ 696 if (label[0] == 0x05) 697 label[0] = 0xE5; 698 699 /* Check for illegal characters */ 700 if (!oklabel(label)) 701 label[0] = 0; 702 703 /* Remove any trailing spaces */ 704 for (i=LABEL_LENGTH-1; i>=0; --i) { 705 if (label[i] == ' ') 706 label[i] = 0; 707 else 708 break; 709 } 710 711 /* Convert it to UTF-8 */ 712 encoding = GetDefaultDOSEncoding(); 713 cfstr = CFStringCreateWithCString(NULL, label, encoding); 714 if (cfstr == NULL && encoding != kCFStringEncodingDOSLatin1) 715 cfstr = CFStringCreateWithCString(NULL, label, kCFStringEncodingDOSLatin1); 716 if (cfstr == NULL) 717 labelUTF8[0] = 0; 718 else { 719 CFMutableStringRef mutable; 720 721 mutable = CFStringCreateMutableCopy(NULL, 0, cfstr); 722 if (mutable != NULL) { 723 CFStringNormalize(mutable, kCFStringNormalizationFormD); 724 CFStringGetCString(mutable, labelUTF8, sizeof(labelUTF8), kCFStringEncodingUTF8); 725 CFRelease(mutable); 726 } 727 728 CFRelease(cfstr); 729 } 730 731 /* At this point, labelUTF8 should contain a correctly formatted name (possibly empty) */ 732 write(1, labelUTF8, strlen(labelUTF8)); 733} 734 735/* 736 * Based from newfs_msdos....to support the same 'functionality'...thanks 737 */ 738 739/* 740 * Check a volume label. 741 */ 742static int 743oklabel(const char *src) 744{ 745 int c, i; 746 747 for (i = 0, c = 0; i <= 11; i++) { 748 c = (u_char)*src++; 749 if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c)) 750 break; 751 } 752 return i && !c; 753} 754 755/* 756 * Make a volume label. 757 */ 758static void 759mklabel(char *dest, const char *src) 760{ 761 int c, i; 762 763 for (i = 0; i < 11; i++) { 764 c = *src ? toupper(*src++) : ' '; 765 *dest++ = !i && c == '\xe5' ? 5 : c; 766 } 767} 768 769static int 770safe_open(char *path, int flags, mode_t mode) 771{ 772 int fd = open(path, flags, mode); 773 774 if (fd < 0) { 775 fprintf(stderr, "%s: open %s failed, %s\n", progname, path, 776 strerror(errno)); 777 exit(FSUR_IO_FAIL); 778 } 779 return(fd); 780} 781 782 783static void 784safe_close(int fd) 785{ 786 if (close(fd)) { 787 fprintf(stderr, "%s: safe_close failed, %s\n", progname, 788 strerror(errno)); 789 exit(FSUR_IO_FAIL); 790 } 791} 792 793void 794safe_execv(const char *args[]) 795{ 796 int pid; 797 union wait status; 798 799 pid = fork(); 800 if (pid == 0) { 801 (void)execv(args[0], (char *const *) args); 802 fprintf(stderr, "%s: execv %s failed, %s\n", progname, args[0], 803 strerror(errno)); 804 exit(FSUR_IO_FAIL); 805 } 806 if (pid == -1) { 807 fprintf(stderr, "%s: fork failed, %s\n", progname, 808 strerror(errno)); 809 exit(FSUR_IO_FAIL); 810 } 811 if (wait4(pid, (int *)&status, 0, NULL) != pid) { 812 fprintf(stderr, "%s: BUG executing %s command\n", progname, 813 args[0]); 814 exit(FSUR_IO_FAIL); 815 } else if (!WIFEXITED(status)) { 816 fprintf(stderr, "%s: %s command aborted by signal %d\n", 817 progname, args[0], WTERMSIG(status)); 818 exit(FSUR_IO_FAIL); 819 } else if (WEXITSTATUS(status)) { 820 fprintf(stderr, "%s: %s command failed, exit status %d: %s\n", 821 progname, args[0], WEXITSTATUS(status), 822 strerror(WEXITSTATUS(status))); 823 exit(FSUR_IO_FAIL); 824 } 825} 826 827 828static void 829safe_read(int fd, void *buf, int nbytes, off_t off) 830{ 831 if (lseek(fd, off, SEEK_SET) == -1) { 832 fprintf(stderr, "%s: device seek error @ %qu, %s\n", progname, 833 off, strerror(errno)); 834 exit(FSUR_IO_FAIL); 835 } 836 if (read(fd, buf, nbytes) != nbytes) { 837 fprintf(stderr, "%s: device safe_read error @ %qu, %s\n", progname, 838 off, strerror(errno)); 839 exit(FSUR_IO_FAIL); 840 } 841} 842 843 844void 845safe_write(int fd, char *buf, int nbytes, off_t off) 846{ 847 if (lseek(fd, off, SEEK_SET) == -1) { 848 fprintf(stderr, "%s: device seek error @ %qu, %s\n", progname, 849 off, strerror(errno)); 850 exit(FSUR_IO_FAIL); 851 } 852 if (write(fd, buf, nbytes) != nbytes) { 853 fprintf(stderr, "%s: write failed, %s\n", progname, 854 strerror(errno)); 855 exit(FSUR_IO_FAIL); 856 } 857} 858 859 860/* Return non-zero if the file system is not yet loaded. */ 861static int checkLoadable(void) 862{ 863 int error; 864 struct vfsconf vfc; 865 866 error = getvfsbyname(FS_TYPE, &vfc); 867 868 return error; 869} 870 871/* end of DOS.util.c */ 872